SpyBara
Go Premium

Documentation 2026-10-07 23:59 UTC to 2026-10-08 23:58 UTC

63 files changed +3,981 −3,819. View all changes and history on the product overview
2026
Sun 11 03:59 Sat 10 23:59 Fri 9 23:59 Thu 8 23:58 Wed 7 23:59 Tue 6 22:57 Mon 5 22:59 Sun 4 23:58 Sat 3 23:58 Fri 2 23:57 Thu 1 23:57
Details

16 16 

17You are unable to concurrently run your requests beyond the rate limits shown in the API console.17You are unable to concurrently run your requests beyond the rate limits shown in the API console.

18 18 

19```pythonXAI

20import asyncio

21import os

22 

23from xai_sdk import AsyncClient

24from xai_sdk.chat import Response, user

25 

26async def main():

27 client = AsyncClient(

28 api_key=os.getenv("XAI_API_KEY"),

29 timeout=3600, # Override default timeout with longer timeout for reasoning models

30 )

31 

32 model = "grok-4.7"

33 requests = [

34 "Tell me a joke",

35 "Write a funny haiku",

36 "Generate a funny X post",

37 "Say something unhinged",

38 ]

39 # Define a semaphore to limit concurrent requests (e.g., max 2 concurrent requests at a time)

40 max_in_flight_requests = 2

41 semaphore = asyncio.Semaphore(max_in_flight_requests)

42 

43 async def process_request(request) -> Response:

44 async with semaphore:

45 print(f"Processing request: {request}")

46 chat = client.chat.create(model=model, max_tokens=100)

47 chat.append(user(request))

48 return await chat.sample()

49 

50 tasks = []

51 for request in requests:

52 tasks.append(process_request(request))

53 

54 responses = await asyncio.gather(*tasks)

55 for i, response in enumerate(responses):

56 print(f"Total tokens used for response {i}: {response.usage.total_tokens}")

57 

58if __name__ == "__main__":

59 asyncio.run(main())

60```

61 

62```pythonOpenAISDK19```pythonOpenAISDK

63import asyncio20import asyncio

64import os21import os


116if __name__ == "__main__":73if __name__ == "__main__":

117 asyncio.run(main())74 asyncio.run(main())

118```75```

76 

77```pythonXAI

78import asyncio

79import os

80 

81from xai_sdk import AsyncClient

82from xai_sdk.chat import Response, user

83 

84async def main():

85 client = AsyncClient(

86 api_key=os.getenv("XAI_API_KEY"),

87 timeout=3600, # Override default timeout with longer timeout for reasoning models

88 )

89 

90 model = "grok-4.7"

91 requests = [

92 "Tell me a joke",

93 "Write a funny haiku",

94 "Generate a funny X post",

95 "Say something unhinged",

96 ]

97 # Define a semaphore to limit concurrent requests (e.g., max 2 concurrent requests at a time)

98 max_in_flight_requests = 2

99 semaphore = asyncio.Semaphore(max_in_flight_requests)

100 

101 async def process_request(request) -> Response:

102 async with semaphore:

103 print(f"Processing request: {request}")

104 chat = client.chat.create(model=model, max_tokens=100)

105 chat.append(user(request))

106 return await chat.sample()

107 

108 tasks = []

109 for request in requests:

110 tasks.append(process_request(request))

111 

112 responses = await asyncio.gather(*tasks)

113 for i, response in enumerate(responses):

114 print(f"Total tokens used for response {i}: {response.usage.total_tokens}")

115 

116if __name__ == "__main__":

117 asyncio.run(main())

118```

Details

67 }'67 }'

68```68```

69 69 

70```pythonXAI

71from xai_sdk import Client

72 

73client = Client()

74 

75# Create a batch with a descriptive name

76batch = client.batch.create(batch_name="customer_feedback_analysis")

77print(f"Created batch: {batch.batch_id}")

78 

79# Store the batch_id for later use

80batch_id = batch.batch_id

81```

82 

83```javascriptWithoutSDK70```javascriptWithoutSDK

84// Create a batch with a descriptive name71// Create a batch with a descriptive name

85const response = await fetch("https://api.x.ai/v1/batches", {72const response = await fetch("https://api.x.ai/v1/batches", {


97const batchId = batch.batch_id;84const batchId = batch.batch_id;

98```85```

99 86 

100## Step 2: Add requests to the batch

101 

102With your batch created, you can now add requests to it. Each request will be processed asynchronously.

103 

104**With the xAI SDK, adding batch requests is simple:** use `chat.create()` for text, `image.prepare()` for images, `video.prepare()` for videos, or `video.prepare_extension()` for video extensions, then pass them as a list. You can also upload a [JSONL file](#jsonl-file-upload) if you prefer.

105 

106**Important:** Assign a unique `batch_request_id` to each request. This ID lets you match results back to their original requests, which becomes important when you're processing hundreds or thousands of items. If you don't provide an ID, we generate a UUID for you. Using your own IDs is useful for idempotency (ensuring a request is only processed once) and for linking batch requests to records in your own system.

107 

108```pythonXAI87```pythonXAI

109from xai_sdk import Client88from xai_sdk import Client

110from xai_sdk.chat import system, user

111from xai_sdk.tools import web_search, x_search, mcp

112 89 

113client = Client()90client = Client()

114 91 

115batch_requests = []92# Create a batch with a descriptive name

116 93batch = client.batch.create(batch_name="customer_feedback_analysis")

117# Chat completion with tools94print(f"Created batch: {batch.batch_id}")

118chat = client.chat.create(

119 model="grok-4.3",

120 batch_request_id="chat_001",

121 tools=[web_search(), x_search()],

122)

123chat.append(system("Analyze market sentiment from recent news and posts."))

124chat.append(user("What is the current sentiment around TSLA stock?"))

125batch_requests.append(chat)

126 

127# Image generation

128image_req = client.image.prepare(

129 prompt="A sleek modern laptop on a minimalist desk",

130 model="grok-imagine-image-2.0",

131 batch_request_id="img_001",

132)

133batch_requests.append(image_req)

134 

135# Image edit

136image_edit_req = client.image.prepare(

137 prompt="Add a rainbow in the background",

138 model="grok-imagine-image-2.0",

139 image_url="https://picsum.photos/800",

140 batch_request_id="img_edit_001",

141)

142batch_requests.append(image_edit_req)

143 95 

144# Video generation96# Store the batch_id for later use

145video_req = client.video.prepare(97batch_id = batch.batch_id

146 prompt="A product rotating on a turntable with dramatic lighting",98```

147 model="grok-imagine-video-1.5",

148 batch_request_id="vid_001",

149)

150batch_requests.append(video_req)

151 99 

152# Video edit100## Step 2: Add requests to the batch

153video_edit_req = client.video.prepare(

154 prompt="Make it slow motion",

155 model="grok-imagine-video",

156 video_url="https://lorem.video/cat_360p_3s",

157 batch_request_id="vid_edit_001",

158)

159batch_requests.append(video_edit_req)

160 101 

161# Video extension102With your batch created, you can now add requests to it. Each request will be processed asynchronously.

162video_ext_req = client.video.prepare_extension(

163 prompt="The camera slowly pans to reveal a sunset behind the mountains",

164 model="grok-imagine-video",

165 video_url="https://lorem.video/cat_360p_3s",

166 duration=6,

167 batch_request_id="vid_ext_001",

168)

169batch_requests.append(video_ext_req)

170 103 

171# Remote MCP104**With the xAI SDK, adding batch requests is simple:** use `chat.create()` for text, `image.prepare()` for images, `video.prepare()` for videos, or `video.prepare_extension()` for video extensions, then pass them as a list. You can also upload a [JSONL file](#jsonl-file-upload) if you prefer.

172mcp_chat = client.chat.create(

173 model="grok-4.3",

174 batch_request_id="mcp_001",

175 tools=[mcp(server_url="https://mcp.deepwiki.com/mcp")],

176)

177mcp_chat.append(user("What does the xai-sdk-python repo do?"))

178batch_requests.append(mcp_chat)

179 105 

180# Add all requests to the batch106**Important:** Assign a unique `batch_request_id` to each request. This ID lets you match results back to their original requests, which becomes important when you're processing hundreds or thousands of items. If you don't provide an ID, we generate a UUID for you. Using your own IDs is useful for idempotency (ensuring a request is only processed once) and for linking batch requests to records in your own system.

181client.batch.add(batch_id=batch.batch_id, batch_requests=batch_requests)

182print(f"Added {len(batch_requests)} requests to batch")

183```

184 107 

185```bash108```bash

186curl -X POST https://api.x.ai/v1/batches/{batch_id}/requests \\109curl -X POST https://api.x.ai/v1/batches/{batch_id}/requests \\


318console.log(\`Added \${batchRequests.length} requests to batch\`);241console.log(\`Added \${batchRequests.length} requests to batch\`);

319```242```

320 243 

244```pythonXAI

245from xai_sdk import Client

246from xai_sdk.chat import system, user

247from xai_sdk.tools import web_search, x_search, mcp

248 

249client = Client()

250 

251batch_requests = []

252 

253# Chat completion with tools

254chat = client.chat.create(

255 model="grok-4.3",

256 batch_request_id="chat_001",

257 tools=[web_search(), x_search()],

258)

259chat.append(system("Analyze market sentiment from recent news and posts."))

260chat.append(user("What is the current sentiment around TSLA stock?"))

261batch_requests.append(chat)

262 

263# Image generation

264image_req = client.image.prepare(

265 prompt="A sleek modern laptop on a minimalist desk",

266 model="grok-imagine-image-2.0",

267 batch_request_id="img_001",

268)

269batch_requests.append(image_req)

270 

271# Image edit

272image_edit_req = client.image.prepare(

273 prompt="Add a rainbow in the background",

274 model="grok-imagine-image-2.0",

275 image_url="https://picsum.photos/800",

276 batch_request_id="img_edit_001",

277)

278batch_requests.append(image_edit_req)

279 

280# Video generation

281video_req = client.video.prepare(

282 prompt="A product rotating on a turntable with dramatic lighting",

283 model="grok-imagine-video-1.5",

284 batch_request_id="vid_001",

285)

286batch_requests.append(video_req)

287 

288# Video edit

289video_edit_req = client.video.prepare(

290 prompt="Make it slow motion",

291 model="grok-imagine-video",

292 video_url="https://lorem.video/cat_360p_3s",

293 batch_request_id="vid_edit_001",

294)

295batch_requests.append(video_edit_req)

296 

297# Video extension

298video_ext_req = client.video.prepare_extension(

299 prompt="The camera slowly pans to reveal a sunset behind the mountains",

300 model="grok-imagine-video",

301 video_url="https://lorem.video/cat_360p_3s",

302 duration=6,

303 batch_request_id="vid_ext_001",

304)

305batch_requests.append(video_ext_req)

306 

307# Remote MCP

308mcp_chat = client.chat.create(

309 model="grok-4.3",

310 batch_request_id="mcp_001",

311 tools=[mcp(server_url="https://mcp.deepwiki.com/mcp")],

312)

313mcp_chat.append(user("What does the xai-sdk-python repo do?"))

314batch_requests.append(mcp_chat)

315 

316# Add all requests to the batch

317client.batch.add(batch_id=batch.batch_id, batch_requests=batch_requests)

318print(f"Added {len(batch_requests)} requests to batch")

319```

320 

321## Step 3: Monitor batch progress321## Step 3: Monitor batch progress

322 322 

323After adding requests, they begin processing in the background. Since batch processing is asynchronous, you need to poll the batch status to know when results are ready.323After adding requests, they begin processing in the background. Since batch processing is asynchronous, you need to poll the batch status to know when results are ready.


340# }340# }

341```341```

342 342 

343```javascriptWithoutSDK

344// Poll until all requests are processed

345console.log("Waiting for batch to complete...");

346const interval = setInterval(async () => {

347 const response = await fetch(

348 \`https://api.x.ai/v1/batches/\${batchId}\`,

349 { headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` } }

350 );

351 const batch = await response.json();

352 

353 const { num_pending, num_success, num_error, num_requests } = batch.state;

354 const completed = num_success + num_error;

355 console.log(\`Progress: \${completed}/\${num_requests} complete, \${num_pending} pending\`);

356 

357 if (num_requests > 0 && num_pending === 0) {

358 clearInterval(interval);

359 console.log("Batch processing complete!");

360 }

361 // Wait before polling again (avoid hammering the API)

362}, 5000);

363```

364 

343```pythonXAI365```pythonXAI

344import time366import time

345from xai_sdk import Client367from xai_sdk import Client


365 time.sleep(5)387 time.sleep(5)

366```388```

367 389 

368```javascriptWithoutSDK

369// Poll until all requests are processed

370console.log("Waiting for batch to complete...");

371const interval = setInterval(async () => {

372 const response = await fetch(

373 \`https://api.x.ai/v1/batches/\${batchId}\`,

374 { headers: { Authorization: \`Bearer \${process.env.XAI_API_KEY}\` } }

375 );

376 const batch = await response.json();

377 

378 const { num_pending, num_success, num_error, num_requests } = batch.state;

379 const completed = num_success + num_error;

380 console.log(\`Progress: \${completed}/\${num_requests} complete, \${num_pending} pending\`);

381 

382 if (num_requests > 0 && num_pending === 0) {

383 clearInterval(interval);

384 console.log("Batch processing complete!");

385 }

386 // Wait before polling again (avoid hammering the API)

387}, 5000);

388```

389 

390### Understanding batch states390### Understanding batch states

391 391 

392The Batch API tracks state at two levels: the **batch level** and the **individual request level**.392The Batch API tracks state at two levels: the **batch level** and the **individual request level**.


425 425 

426**Pagination:** Results are returned in pages. Use the `limit` parameter to control page size and `pagination_token` to fetch subsequent pages. When `pagination_token` is `None`, you've reached the end.426**Pagination:** Results are returned in pages. Use the `limit` parameter to control page size and `pagination_token` to fetch subsequent pages. When `pagination_token` is `None`, you've reached the end.

427 427 

428```pythonXAI

429from xai_sdk import Client

430 

431client = Client()

432 

433# Paginate through all results

434all_succeeded = []

435all_failed = []

436pagination_token = None

437 

438while True:

439 # Fetch a page of results (limit controls page size)

440 page = client.batch.list_batch_results(

441 batch_id=batch.batch_id,

442 limit=100,

443 pagination_token=pagination_token,

444 )

445

446 # Collect results from this page

447 all_succeeded.extend(page.succeeded)

448 all_failed.extend(page.failed)

449

450 # Check if there are more pages

451 if page.pagination_token is None:

452 break

453 pagination_token = page.pagination_token

454 

455# Process results - handle different response types

456print(f"Successfully processed: {len(all_succeeded)} requests")

457for result in all_succeeded:

458 rid = result.batch_request_id

459 resp = result.proto.response

460 

461 if resp.HasField("completion_response"):

462 # Chat completion response

463 print(f"[{rid}] {result.response.content}")

464 print(f" Tokens used: {result.response.usage.total_tokens}")

465 elif resp.HasField("image_response"):

466 # Image generation response

467 print(f"[{rid}] Image URL: {result.image_response.url}")

468 elif resp.HasField("video_response"):

469 # Video generation response

470 print(f"[{rid}] Video URL: {result.video_response.url}")

471 

472if all_failed:

473 print(f"\\nFailed: {len(all_failed)} requests")

474 for result in all_failed:

475 print(f"[{result.batch_request_id}] Error: {result.error_message}")

476```

477 

478```bash428```bash

479# Fetch first page429# Fetch first page

480curl "https://api.x.ai/v1/batches/{batch_id}/results?limit=100" \\430curl "https://api.x.ai/v1/batches/{batch_id}/results?limit=100" \\


538}488}

539```489```

540 490 

491```pythonXAI

492from xai_sdk import Client

493 

494client = Client()

495 

496# Paginate through all results

497all_succeeded = []

498all_failed = []

499pagination_token = None

500 

501while True:

502 # Fetch a page of results (limit controls page size)

503 page = client.batch.list_batch_results(

504 batch_id=batch.batch_id,

505 limit=100,

506 pagination_token=pagination_token,

507 )

508

509 # Collect results from this page

510 all_succeeded.extend(page.succeeded)

511 all_failed.extend(page.failed)

512

513 # Check if there are more pages

514 if page.pagination_token is None:

515 break

516 pagination_token = page.pagination_token

517 

518# Process results - handle different response types

519print(f"Successfully processed: {len(all_succeeded)} requests")

520for result in all_succeeded:

521 rid = result.batch_request_id

522 resp = result.proto.response

523 

524 if resp.HasField("completion_response"):

525 # Chat completion response

526 print(f"[{rid}] {result.response.content}")

527 print(f" Tokens used: {result.response.usage.total_tokens}")

528 elif resp.HasField("image_response"):

529 # Image generation response

530 print(f"[{rid}] Image URL: {result.image_response.url}")

531 elif resp.HasField("video_response"):

532 # Video generation response

533 print(f"[{rid}] Video URL: {result.video_response.url}")

534 

535if all_failed:

536 print(f"\\nFailed: {len(all_failed)} requests")

537 for result in all_failed:

538 print(f"[{result.batch_request_id}] Error: {result.error_message}")

539```

540 

541## Additional operations541## Additional operations

542 542 

543Beyond the core workflow, the Batch API provides additional operations for managing your batches.543Beyond the core workflow, the Batch API provides additional operations for managing your batches.


551 -H "Authorization: Bearer $XAI_API_KEY"551 -H "Authorization: Bearer $XAI_API_KEY"

552```552```

553 553 

554```pythonXAI

555from xai_sdk import Client

556 

557client = Client()

558 

559# Cancel processing

560cancelled_batch = client.batch.cancel(batch_id=batch.batch_id)

561print(f"Cancelled batch: {cancelled_batch.batch_id}")

562print(f"Completed before cancellation: {cancelled_batch.state.num_success} requests")

563```

564 

565```javascriptWithoutSDK554```javascriptWithoutSDK

566// Cancel processing555// Cancel processing

567const response = await fetch(556const response = await fetch(


573console.log(\`Completed before cancellation: \${cancelledBatch.state.num_success} requests\`);562console.log(\`Completed before cancellation: \${cancelledBatch.state.num_success} requests\`);

574```563```

575 564 

565```pythonXAI

566from xai_sdk import Client

567 

568client = Client()

569 

570# Cancel processing

571cancelled_batch = client.batch.cancel(batch_id=batch.batch_id)

572print(f"Cancelled batch: {cancelled_batch.batch_id}")

573print(f"Completed before cancellation: {cancelled_batch.state.num_success} requests")

574```

575 

576### List all batches576### List all batches

577 577 

578View all batches belonging to your team. Batches are retained until they expire (check the `expires_at` field). This endpoint supports the same `limit` and `pagination_token` parameters for paginating through large lists.578View all batches belonging to your team. Batches are retained until they expire (check the `expires_at` field). This endpoint supports the same `limit` and `pagination_token` parameters for paginating through large lists.


582 -H "Authorization: Bearer $XAI_API_KEY"582 -H "Authorization: Bearer $XAI_API_KEY"

583```583```

584 584 

585```pythonXAI

586from xai_sdk import Client

587 

588client = Client()

589 

590# List recent batches

591response = client.batch.list(limit=20)

592 

593for batch in response.batches:

594 status = "complete" if batch.state.num_pending == 0 else "processing"

595 print(f"{batch.name} ({batch.batch_id}): {status}")

596```

597 

598```javascriptWithoutSDK585```javascriptWithoutSDK

599// List recent batches586// List recent batches

600const response = await fetch(587const response = await fetch(


609}596}

610```597```

611 598 

599```pythonXAI

600from xai_sdk import Client

601 

602client = Client()

603 

604# List recent batches

605response = client.batch.list(limit=20)

606 

607for batch in response.batches:

608 status = "complete" if batch.state.num_pending == 0 else "processing"

609 print(f"{batch.name} ({batch.batch_id}): {status}")

610```

611 

612### Check individual request status612### Check individual request status

613 613 

614For detailed tracking, you can inspect the metadata for each request in a batch. This shows the status, timing, and other details for individual requests. This endpoint supports the same `limit` and `pagination_token` parameters for paginating through large batches.614For detailed tracking, you can inspect the metadata for each request in a batch. This shows the status, timing, and other details for individual requests. This endpoint supports the same `limit` and `pagination_token` parameters for paginating through large batches.


618 -H "Authorization: Bearer $XAI_API_KEY"618 -H "Authorization: Bearer $XAI_API_KEY"

619```619```

620 620 

621```pythonXAI

622from xai_sdk import Client

623 

624client = Client()

625 

626# Get metadata for individual requests

627metadata = client.batch.list_batch_requests(batch_id=batch.batch_id)

628 

629for request in metadata.batch_request_metadata:

630 print(f"Request {request.batch_request_id}: {request.state}")

631```

632 

633```javascriptWithoutSDK621```javascriptWithoutSDK

634// Get metadata for individual requests622// Get metadata for individual requests

635const response = await fetch(623const response = await fetch(


643}631}

644```632```

645 633 

634```pythonXAI

635from xai_sdk import Client

636 

637client = Client()

638 

639# Get metadata for individual requests

640metadata = client.batch.list_batch_requests(batch_id=batch.batch_id)

641 

642for request in metadata.batch_request_metadata:

643 print(f"Request {request.batch_request_id}: {request.state}")

644```

645 

646### Track costs646### Track costs

647 647 

648Each batch tracks the total processing cost. Access the cost breakdown after processing to understand your spending. For pricing details, see [Batch API Pricing on the Pricing page](/developers/pricing#batch-api-pricing).648Each batch tracks the total processing cost. Access the cost breakdown after processing to understand your spending. For pricing details, see [Batch API Pricing on the Pricing page](/developers/pricing#batch-api-pricing).


656# Cost is returned in ticks (1e-10 USD) for precision656# Cost is returned in ticks (1e-10 USD) for precision

657```657```

658 658 

659```pythonXAI

660from xai_sdk import Client

661 

662client = Client()

663 

664# Get batch with cost information

665batch = client.batch.get(batch_id=batch.batch_id)

666 

667# Cost is returned in ticks (1e-10 USD) for precision

668total_cost_usd = batch.cost_breakdown.total_cost_usd_ticks / 1e10

669print("Total cost: $%.4f" % total_cost_usd)

670```

671 

672```javascriptWithoutSDK659```javascriptWithoutSDK

673// Get batch with cost information660// Get batch with cost information

674const response = await fetch(661const response = await fetch(


685console.log(\`Total cost: $\${(totalTicks / 1e10).toFixed(4)}\`);672console.log(\`Total cost: $\${(totalTicks / 1e10).toFixed(4)}\`);

686```673```

687 674 

688## Complete example

689 

690This end-to-end example demonstrates a realistic batch workflow: analyzing customer feedback at scale. It creates a batch, submits feedback items for sentiment analysis, waits for processing, and outputs the results. For simplicity, this example doesn't paginate results—see [Step 4](#step-4-retrieve-results) for pagination when processing larger batches.

691 

692```pythonXAI675```pythonXAI

693import time

694from xai_sdk import Client676from xai_sdk import Client

695from xai_sdk.chat import system, user

696 677 

697client = Client()678client = Client()

698 679 

699# Sample dataset: customer feedback to analyze680# Get batch with cost information

700feedback_data = [681batch = client.batch.get(batch_id=batch.batch_id)

701 {"id": "fb_001", "text": "Absolutely love this product! Best purchase ever."},

702 {"id": "fb_002", "text": "Delivery was late and the packaging was damaged."},

703 {"id": "fb_003", "text": "Works fine, nothing special to report."},

704 {"id": "fb_004", "text": "Customer support was incredibly helpful!"},

705 {"id": "fb_005", "text": "The app keeps crashing on my phone."},

706]

707 

708# Step 1: Create a batch

709print("Creating batch...")

710batch = client.batch.create(batch_name="feedback_sentiment_analysis")

711print(f"Batch created: {batch.batch_id}")

712 

713# Step 2: Build and add requests

714print("\\nAdding requests...")

715batch_requests = []

716for item in feedback_data:

717 chat = client.chat.create(

718 model="grok-4.3",

719 batch_request_id=item["id"],

720 )

721 chat.append(system(

722 "Analyze the sentiment of the customer feedback. "

723 "Respond with exactly one word: positive, negative, or neutral."

724 ))

725 chat.append(user(item["text"]))

726 batch_requests.append(chat)

727 

728client.batch.add(batch_id=batch.batch_id, batch_requests=batch_requests)

729print(f"Added {len(batch_requests)} requests")

730 

731# Step 3: Wait for completion

732print("\\nProcessing...")

733while True:

734 batch = client.batch.get(batch_id=batch.batch_id)

735 pending = batch.state.num_pending

736 completed = batch.state.num_success + batch.state.num_error

737

738 print(f" {completed}/{batch.state.num_requests} complete")

739

740 if pending == 0:

741 break

742 time.sleep(2)

743 

744# Step 4: Retrieve and display results

745print("\\n--- Results ---")

746results = client.batch.list_batch_results(batch_id=batch.batch_id)

747 

748# Create a lookup for original feedback text

749feedback_lookup = {item["id"]: item["text"] for item in feedback_data}

750 682 

751for result in results.succeeded:683# Cost is returned in ticks (1e-10 USD) for precision

752 original_text = feedback_lookup.get(result.batch_request_id, "")684total_cost_usd = batch.cost_breakdown.total_cost_usd_ticks / 1e10

753 sentiment = result.response.content.strip().lower()685print("Total cost: $%.4f" % total_cost_usd)

754 print(f"[{sentiment.upper()}] {original_text[:50]}...")686```

755 687 

756# Report any failures688## Complete example

757if results.failed:

758 print("\\n--- Errors ---")

759 for result in results.failed:

760 print(f"[{result.batch_request_id}] {result.error_message}")

761 689 

762# Display cost690This end-to-end example demonstrates a realistic batch workflow: analyzing customer feedback at scale. It creates a batch, submits feedback items for sentiment analysis, waits for processing, and outputs the results. For simplicity, this example doesn't paginate results—see [Step 4](#step-4-retrieve-results) for pagination when processing larger batches.

763cost_usd = batch.cost_breakdown.total_cost_usd_ticks / 1e10

764print("\\nTotal cost: $%.4f" % cost_usd)

765```

766 691 

767```javascriptWithoutSDK692```javascriptWithoutSDK

768const BASE_URL = "https://api.x.ai/v1";693const BASE_URL = "https://api.x.ai/v1";


860}, 2000);785}, 2000);

861```786```

862 787 

788```pythonXAI

789import time

790from xai_sdk import Client

791from xai_sdk.chat import system, user

792 

793client = Client()

794 

795# Sample dataset: customer feedback to analyze

796feedback_data = [

797 {"id": "fb_001", "text": "Absolutely love this product! Best purchase ever."},

798 {"id": "fb_002", "text": "Delivery was late and the packaging was damaged."},

799 {"id": "fb_003", "text": "Works fine, nothing special to report."},

800 {"id": "fb_004", "text": "Customer support was incredibly helpful!"},

801 {"id": "fb_005", "text": "The app keeps crashing on my phone."},

802]

803 

804# Step 1: Create a batch

805print("Creating batch...")

806batch = client.batch.create(batch_name="feedback_sentiment_analysis")

807print(f"Batch created: {batch.batch_id}")

808 

809# Step 2: Build and add requests

810print("\\nAdding requests...")

811batch_requests = []

812for item in feedback_data:

813 chat = client.chat.create(

814 model="grok-4.3",

815 batch_request_id=item["id"],

816 )

817 chat.append(system(

818 "Analyze the sentiment of the customer feedback. "

819 "Respond with exactly one word: positive, negative, or neutral."

820 ))

821 chat.append(user(item["text"]))

822 batch_requests.append(chat)

823 

824client.batch.add(batch_id=batch.batch_id, batch_requests=batch_requests)

825print(f"Added {len(batch_requests)} requests")

826 

827# Step 3: Wait for completion

828print("\\nProcessing...")

829while True:

830 batch = client.batch.get(batch_id=batch.batch_id)

831 pending = batch.state.num_pending

832 completed = batch.state.num_success + batch.state.num_error

833

834 print(f" {completed}/{batch.state.num_requests} complete")

835

836 if pending == 0:

837 break

838 time.sleep(2)

839 

840# Step 4: Retrieve and display results

841print("\\n--- Results ---")

842results = client.batch.list_batch_results(batch_id=batch.batch_id)

843 

844# Create a lookup for original feedback text

845feedback_lookup = {item["id"]: item["text"] for item in feedback_data}

846 

847for result in results.succeeded:

848 original_text = feedback_lookup.get(result.batch_request_id, "")

849 sentiment = result.response.content.strip().lower()

850 print(f"[{sentiment.upper()}] {original_text[:50]}...")

851 

852# Report any failures

853if results.failed:

854 print("\\n--- Errors ---")

855 for result in results.failed:

856 print(f"[{result.batch_request_id}] {result.error_message}")

857 

858# Display cost

859cost_usd = batch.cost_breakdown.total_cost_usd_ticks / 1e10

860print("\\nTotal cost: $%.4f" % cost_usd)

861```

862 

863## JSONL File Upload863## JSONL File Upload

864 864 

865As an alternative to adding requests via the SDK, you can create batches by uploading a JSONL file. This is useful when generating requests from scripts, pipelines, or external tools.865As an alternative to adding requests via the SDK, you can create batches by uploading a JSONL file. This is useful when generating requests from scripts, pipelines, or external tools.


895 895 

896Upload the file via the [Files API](/developers/files), then create a batch referencing it:896Upload the file via the [Files API](/developers/files), then create a batch referencing it:

897 897 

898```pythonXAI

899from xai_sdk import Client

900 

901client = Client()

902 

903# Upload the JSONL file

904file = client.files.upload(

905 file=open("batch_requests.jsonl", "rb"),

906)

907 

908# Create a batch with the file ID

909batch = client.batch.create(

910 batch_name="sentiment_analysis",

911 input_file_id=file.id,

912)

913print(f"Created batch: {batch.batch_id}")

914```

915 

916```bash898```bash

917# Upload the JSONL file899# Upload the JSONL file

918curl -X POST https://api.x.ai/v1/files \\900curl -X POST https://api.x.ai/v1/files \\


957console.log(\`Created batch: \${batch.batch_id}\`);939console.log(\`Created batch: \${batch.batch_id}\`);

958```940```

959 941 

942```pythonXAI

943from xai_sdk import Client

944 

945client = Client()

946 

947# Upload the JSONL file

948file = client.files.upload(

949 file=open("batch_requests.jsonl", "rb"),

950)

951 

952# Create a batch with the file ID

953batch = client.batch.create(

954 batch_name="sentiment_analysis",

955 input_file_id=file.id,

956)

957print(f"Created batch: {batch.batch_id}")

958```

959 

960The file is processed asynchronously in the background. If any line is invalid, the batch is cancelled with an error message. Monitor progress and retrieve results the same way as inline batches.960The file is processed asynchronously in the background. If any line is invalid, the batch is cancelled with an error message. Monitor progress and retrieve results the same way as inline batches.

961 961 

962File-based batches are sealed after creation — you cannot add more requests via `AddBatchRequests`. Maximum file size is **200 MB** with up to **50,000** requests. Each `custom_id` must be unique within the file.962File-based batches are sealed after creation — you cannot add more requests via `AddBatchRequests`. Maximum file size is **200 MB** with up to **50,000** requests. Each `custom_id` must be unique within the file.


985## Related985## Related

986 986 

987* [API Reference: Batch endpoints](/developers/rest-api-reference/inference/batches#create-a-new-batch)987* [API Reference: Batch endpoints](/developers/rest-api-reference/inference/batches#create-a-new-batch)

988* [gRPC Reference: Batch Management](/developers/grpc-api-reference/batches)

989* [Pricing — Batch API Pricing](/developers/pricing#batch-api-pricing)988* [Pricing — Batch API Pricing](/developers/pricing#batch-api-pricing)

990* [xAI Python SDK](https://github.com/xai-org/xai-sdk-python)989* [xAI Python SDK](https://github.com/xai-org/xai-sdk-python)

Details

29 29 

30Send the conversation you want to compact. The response contains a single compaction item that stands in for the entire prior conversation — you can safely drop the original messages from your client-side state, use the compaction item as the head of your next request, and append your new user turn after it.30Send the conversation you want to compact. The response contains a single compaction item that stands in for the entire prior conversation — you can safely drop the original messages from your client-side state, use the compaction item as the head of your next request, and append your new user turn after it.

31 31 

32```bash customLanguage="bash"

33# Step 1 — compact the long conversation

34curl -s https://api.x.ai/v1/responses/compact \

35 -H "Content-Type: application/json" \

36 -H "Authorization: Bearer $XAI_API_KEY" \

37 -d '{

38 "model": "grok-4.7",

39 "input": [

40 {"role": "system", "content": "You are a concise and knowledgeable science tutor."},

41 {"role": "user", "content": "What is the Higgs boson and why is it important?"},

42 {"role": "assistant", "content": "The Higgs boson is an elementary particle..."},

43 {"role": "user", "content": "How does the Higgs mechanism actually work?"},

44 {"role": "assistant", "content": "The Higgs mechanism works through spontaneous symmetry breaking..."}

45 ]

46 }'

47 

48# Step 2 — continue the conversation using the compacted output

49curl -s https://api.x.ai/v1/responses \

50 -H "Content-Type: application/json" \

51 -H "Authorization: Bearer $XAI_API_KEY" \

52 -d '{

53 "model": "grok-4.7",

54 "input": [

55 {

56 "type": "compaction",

57 "id": "cmp_abc123",

58 "encrypted_content": "<paste encrypted_content from step 1>"

59 },

60 {"role": "user", "content": "Based on our earlier conversation, what gives particles their mass?"}

61 ]

62 }'

63```

64 

65```python customLanguage="pythonXAI"

66import os

67from xai_sdk import Client

68from xai_sdk.chat import system, user

69 

70client = Client(api_key=os.environ["XAI_API_KEY"])

71 

72# Build up a chat normally — system prompt plus a few user/assistant turns.

73# use_encrypted_content=True is recommended for reasoning models so the model's

74# reasoning content from prior turns is preserved through the compaction.

75chat = client.chat.create(model="grok-4.7", use_encrypted_content=True)

76chat.append(system("You are a concise and knowledgeable science tutor."))

77 

78chat.append(user("What is the Higgs boson and why is it important?"))

79chat.append(chat.sample())

80 

81chat.append(user("How does the Higgs mechanism actually work?"))

82chat.append(chat.sample())

83 

84# ... many more turns ...

85 

86# Step 1 — compact the conversation. Pass the chat's accumulated messages

87# straight into compact_context.

88compact = client.chat.compact_context(

89 model="grok-4.7",

90 messages=chat.messages,

91)

92print(f"Compaction ID: {compact.id}")

93print(f"Dropped messages: {compact.dropped_message_count}")

94print(f"Tokens used: {compact.usage.total_tokens}")

95 

96# Step 2 — continue the conversation. chat.append(compact) clears the

97# in-memory message list on the chat object and seeds it with just the

98# compaction blob, so subsequent chat.sample() calls run on top of the

99# compacted context instead of replaying the full prior history.

100chat.append(compact)

101chat.append(user("Based on our earlier conversation, what gives particles their mass?"))

102print(chat.sample().content)

103```

104 

105```python customLanguage="pythonOpenAISDK"32```python customLanguage="pythonOpenAISDK"

106import os33import os

107from openai import OpenAI34from openai import OpenAI


139print(followup.output_text)66print(followup.output_text)

140```67```

141 68 

69```bash customLanguage="bash"

70# Step 1 — compact the long conversation

71curl -s https://api.x.ai/v1/responses/compact \

72 -H "Content-Type: application/json" \

73 -H "Authorization: Bearer $XAI_API_KEY" \

74 -d '{

75 "model": "grok-4.7",

76 "input": [

77 {"role": "system", "content": "You are a concise and knowledgeable science tutor."},

78 {"role": "user", "content": "What is the Higgs boson and why is it important?"},

79 {"role": "assistant", "content": "The Higgs boson is an elementary particle..."},

80 {"role": "user", "content": "How does the Higgs mechanism actually work?"},

81 {"role": "assistant", "content": "The Higgs mechanism works through spontaneous symmetry breaking..."}

82 ]

83 }'

84 

85# Step 2 — continue the conversation using the compacted output

86curl -s https://api.x.ai/v1/responses \

87 -H "Content-Type: application/json" \

88 -H "Authorization: Bearer $XAI_API_KEY" \

89 -d '{

90 "model": "grok-4.7",

91 "input": [

92 {

93 "type": "compaction",

94 "id": "cmp_abc123",

95 "encrypted_content": "<paste encrypted_content from step 1>"

96 },

97 {"role": "user", "content": "Based on our earlier conversation, what gives particles their mass?"}

98 ]

99 }'

100```

101 

142```javascript customLanguage="javascriptOpenAISDK"102```javascript customLanguage="javascriptOpenAISDK"

143import OpenAI from "openai";103import OpenAI from "openai";

144 104 


175console.log(followup.output_text);135console.log(followup.output_text);

176```136```

177 137 

138```python customLanguage="pythonXAI"

139import os

140from xai_sdk import Client

141from xai_sdk.chat import system, user

142 

143client = Client(api_key=os.environ["XAI_API_KEY"])

144 

145# Build up a chat normally — system prompt plus a few user/assistant turns.

146# use_encrypted_content=True is recommended for reasoning models so the model's

147# reasoning content from prior turns is preserved through the compaction.

148chat = client.chat.create(model="grok-4.7", use_encrypted_content=True)

149chat.append(system("You are a concise and knowledgeable science tutor."))

150 

151chat.append(user("What is the Higgs boson and why is it important?"))

152chat.append(chat.sample())

153 

154chat.append(user("How does the Higgs mechanism actually work?"))

155chat.append(chat.sample())

156 

157# ... many more turns ...

158 

159# Step 1 — compact the conversation. Pass the chat's accumulated messages

160# straight into compact_context.

161compact = client.chat.compact_context(

162 model="grok-4.7",

163 messages=chat.messages,

164)

165print(f"Compaction ID: {compact.id}")

166print(f"Dropped messages: {compact.dropped_message_count}")

167print(f"Tokens used: {compact.usage.total_tokens}")

168 

169# Step 2 — continue the conversation. chat.append(compact) clears the

170# in-memory message list on the chat object and seeds it with just the

171# compaction blob, so subsequent chat.sample() calls run on top of the

172# compacted context instead of replaying the full prior history.

173chat.append(compact)

174chat.append(user("Based on our earlier conversation, what gives particles their mass?"))

175print(chat.sample().content)

176```

177 

178The xAI SDK also exposes an `AsyncClient` with `await client.chat.compact_context(...)` and `await chat.sample()` for the same flow under `asyncio`.178The xAI SDK also exposes an `AsyncClient` with `await client.chat.compact_context(...)` and `await chat.sample()` for the same flow under `asyncio`.

179 179 

180### Response shape180### Response shape

Details

26 26 

27A code example is provided below, where we retry retrieving the result until it has been processed:27A code example is provided below, where we retry retrieving the result until it has been processed:

28 28 

29```pythonXAI

30import os

31from datetime import timedelta

32 

33from xai_sdk import Client

34from xai_sdk.chat import user, system

35 

36client = Client(api_key=os.getenv('XAI_API_KEY'))

37 

38chat = client.chat.create(

39 model="grok-4.7",

40 messages=[system("You are Zaphod Beeblebrox.")]

41)

42chat.append(user("126/3=?"))

43 

44# Poll the result every 10 seconds for a maximum of 10 minutes

45 

46response = chat.defer(

47 timeout=timedelta(minutes=10), interval=timedelta(seconds=10)

48)

49 

50# Print the result when it is ready

51 

52print(response.content)

53```

54 

55```pythonRequests29```pythonRequests

56import json30import json

57import os31import os


162-H "Authorization: Bearer $XAI_API_KEY"136-H "Authorization: Bearer $XAI_API_KEY"

163```137```

164 138 

139```pythonXAI

140import os

141from datetime import timedelta

142 

143from xai_sdk import Client

144from xai_sdk.chat import user, system

145 

146client = Client(api_key=os.getenv('XAI_API_KEY'))

147 

148chat = client.chat.create(

149 model="grok-4.7",

150 messages=[system("You are Zaphod Beeblebrox.")]

151)

152chat.append(user("126/3=?"))

153 

154# Poll the result every 10 seconds for a maximum of 10 minutes

155 

156response = chat.defer(

157 timeout=timedelta(minutes=10), interval=timedelta(seconds=10)

158)

159 

160# Print the result when it is ready

161 

162print(response.content)

163```

164 

165The response body will be the same as what you would expect with non-deferred chat completions:165The response body will be the same as what you would expect with non-deferred chat completions:

166 166 

167```json167```json

Details

34 34 

35Include your client certificate and private key with every request. Here are examples:35Include your client certificate and private key with every request. Here are examples:

36 36 

37```bash

38curl https://mtls.api.x.ai/v1/chat/completions \\

39 --cert /path/to/client-cert.pem \\

40 --key /path/to/client-key.pem \\

41 -H "Content-Type: application/json" \\

42 -H "Authorization: Bearer $XAI_API_KEY" \\

43 -d '{

44 "messages": [

45 {

46 "role": "user",

47 "content": "Hello, world!"

48 }

49 ],

50 "model": "grok-4.7",

51 "stream": false

52 }'

53```

54 

55```pythonOpenAISDK37```pythonOpenAISDK

56import os38import os

57import httpx39import httpx


77print(completion.choices[0].message.content)59print(completion.choices[0].message.content)

78```60```

79 61 

62```bash

63curl https://mtls.api.x.ai/v1/chat/completions \\

64 --cert /path/to/client-cert.pem \\

65 --key /path/to/client-key.pem \\

66 -H "Content-Type: application/json" \\

67 -H "Authorization: Bearer $XAI_API_KEY" \\

68 -d '{

69 "messages": [

70 {

71 "role": "user",

72 "content": "Hello, world!"

73 }

74 ],

75 "model": "grok-4.7",

76 "stream": false

77 }'

78```

79 

80```javascriptOpenAISDK80```javascriptOpenAISDK

81import OpenAI from 'openai';81import OpenAI from 'openai';

82import https from 'https';82import https from 'https';

Details

23 23 

24Pass `service_tier: "priority"` in your request body. The response includes a `service_tier` field confirming which tier was used.24Pass `service_tier: "priority"` in your request body. The response includes a `service_tier` field confirming which tier was used.

25 25 

26```bash customLanguage="bash"

27curl https://api.x.ai/v1/responses \

28 -H "Authorization: Bearer $XAI_API_KEY" \

29 -H "Content-Type: application/json" \

30 -d '{

31 "model": "grok-4.7",

32 "input": "Explain the Riemann hypothesis in one paragraph.",

33 "service_tier": "priority"

34 }'

35```

36 

37```python customLanguage="pythonXAI"

38import os

39 

40from xai_sdk import Client

41from xai_sdk.chat import user

42 

43client = Client(api_key=os.getenv("XAI_API_KEY"))

44 

45chat = client.chat.create(

46 model="grok-4.7",

47 service_tier="priority",

48)

49chat.append(user("Explain the Riemann hypothesis in one paragraph."))

50 

51response = chat.sample()

52 

53print(response.content)

54print(f"Tier used: {response.service_tier}")

55```

56 

57```python customLanguage="pythonOpenAISDK"26```python customLanguage="pythonOpenAISDK"

58import os27import os

59from openai import OpenAI28from openai import OpenAI


73print(f"Tier used: {response.service_tier}")42print(f"Tier used: {response.service_tier}")

74```43```

75 44 

45```bash customLanguage="bash"

46curl https://api.x.ai/v1/responses \

47 -H "Authorization: Bearer $XAI_API_KEY" \

48 -H "Content-Type: application/json" \

49 -d '{

50 "model": "grok-4.7",

51 "input": "Explain the Riemann hypothesis in one paragraph.",

52 "service_tier": "priority"

53 }'

54```

55 

76```javascript customLanguage="javascriptOpenAISDK"56```javascript customLanguage="javascriptOpenAISDK"

77import OpenAI from "openai";57import OpenAI from "openai";

78 58 


91console.log(`Tier used: ${response.service_tier}`);71console.log(`Tier used: ${response.service_tier}`);

92```72```

93 73 

74```python customLanguage="pythonXAI"

75import os

76 

77from xai_sdk import Client

78from xai_sdk.chat import user

79 

80client = Client(api_key=os.getenv("XAI_API_KEY"))

81 

82chat = client.chat.create(

83 model="grok-4.7",

84 service_tier="priority",

85)

86chat.append(user("Explain the Riemann hypothesis in one paragraph."))

87 

88response = chat.sample()

89 

90print(response.content)

91print(f"Tier used: {response.service_tier}")

92```

93 

94The response includes `"service_tier": "priority"` when the request was served at the priority tier, or `"service_tier": "default"` if it was served at the default tier instead. You are only billed at the priority rate when the response confirms `"priority"`.94The response includes `"service_tier": "priority"` when the request was served at the priority tier, or `"service_tier": "default"` if it was served at the default tier instead. You are only billed at the priority rate when the response confirms `"priority"`.

95 95 

96```json customLanguage="json"96```json customLanguage="json"

Details

6 6 

7The `x-grok-conv-id` HTTP header routes requests with the same conversation ID to the same server. Since cache entries are stored per-server, this maximizes your cache hit rate.7The `x-grok-conv-id` HTTP header routes requests with the same conversation ID to the same server. Since cache entries are stored per-server, this maximizes your cache hit rate.

8 8 

9```bash customLanguage="bash"

10curl https://api.x.ai/v1/chat/completions \

11 -H "Content-Type: application/json" \

12 -H "Authorization: Bearer $XAI_API_KEY" \

13 -H "x-grok-conv-id: conv_abc123" \

14 -d '{

15 "model": "grok-4.7",

16 "messages": [

17 {"role": "system", "content": "You are Grok, a helpful and truthful AI assistant built by xAI."},

18 {"role": "user", "content": "What is prompt caching?"}

19 ]

20 }'

21```

22 

23```python customLanguage="pythonOpenAISDK"9```python customLanguage="pythonOpenAISDK"

24from openai import OpenAI10from openai import OpenAI

25 11 


43print(f"Cached tokens: {response.usage.prompt_tokens_details.cached_tokens}")29print(f"Cached tokens: {response.usage.prompt_tokens_details.cached_tokens}")

44```30```

45 31 

32```bash customLanguage="bash"

33curl https://api.x.ai/v1/chat/completions \

34 -H "Content-Type: application/json" \

35 -H "Authorization: Bearer $XAI_API_KEY" \

36 -H "x-grok-conv-id: conv_abc123" \

37 -d '{

38 "model": "grok-4.7",

39 "messages": [

40 {"role": "system", "content": "You are Grok, a helpful and truthful AI assistant built by xAI."},

41 {"role": "user", "content": "What is prompt caching?"}

42 ]

43 }'

44```

45 

46```javascript customLanguage="javascriptOpenAISDK"46```javascript customLanguage="javascriptOpenAISDK"

47import OpenAI from 'openai';47import OpenAI from 'openai';

48 48 


80 80 

81For the Responses API, use the `prompt_cache_key` field directly in the request body. It functions identically to setting `x-grok-conv-id` — it routes requests to the same server for cache reuse.81For the Responses API, use the `prompt_cache_key` field directly in the request body. It functions identically to setting `x-grok-conv-id` — it routes requests to the same server for cache reuse.

82 82 

83```bash customLanguage="bash"83```javascript customLanguage="javascriptAISDK"

84curl https://api.x.ai/v1/responses \84import { xai } from '@ai-sdk/xai';

85 -H "Content-Type: application/json" \85import { generateText } from 'ai';

86 -H "Authorization: Bearer $XAI_API_KEY" \86 

87 -d '{87const { text, usage } = await generateText({

88 "model": "grok-4.7",88 model: xai.responses('grok-4.7'),

89 "input": "What is prompt caching?",89 prompt: 'What is prompt caching?',

90 "prompt_cache_key": "b79ad29b-b3f9-463c-bca6-041d5058d366"90 providerOptions: {

91 }'91 xai: {

92 promptCacheKey: 'b79ad29b-b3f9-463c-bca6-041d5058d366',

93 },

94 },

95});

96 

97console.log(text);

98console.log(`Total tokens: ${usage.totalTokens}`);

92```99```

93 100 

94```python customLanguage="pythonOpenAISDK"101```python customLanguage="pythonOpenAISDK"


111print(f"Cached tokens: {response.usage.input_tokens_details.cached_tokens}")118print(f"Cached tokens: {response.usage.input_tokens_details.cached_tokens}")

112```119```

113 120 

121```bash customLanguage="bash"

122curl https://api.x.ai/v1/responses \

123 -H "Content-Type: application/json" \

124 -H "Authorization: Bearer $XAI_API_KEY" \

125 -d '{

126 "model": "grok-4.7",

127 "input": "What is prompt caching?",

128 "prompt_cache_key": "b79ad29b-b3f9-463c-bca6-041d5058d366"

129 }'

130```

131 

114```javascript customLanguage="javascriptOpenAISDK"132```javascript customLanguage="javascriptOpenAISDK"

115import OpenAI from 'openai';133import OpenAI from 'openai';

116 134 


132);150);

133```151```

134 152 

135```javascript customLanguage="javascriptAISDK"

136import { xai } from '@ai-sdk/xai';

137import { generateText } from 'ai';

138 

139const { text, usage } = await generateText({

140 model: xai.responses('grok-4.7'),

141 prompt: 'What is prompt caching?',

142 providerOptions: {

143 xai: {

144 promptCacheKey: 'b79ad29b-b3f9-463c-bca6-041d5058d366',

145 },

146 },

147});

148 

149console.log(text);

150console.log(`Total tokens: ${usage.totalTokens}`);

151```

152 

153## Set `x-grok-conv-id` metadata (gRPC API)153## Set `x-grok-conv-id` metadata (gRPC API)

154 154 

155For the gRPC API using the xAI SDK, pass `x-grok-conv-id` as gRPC metadata to enable sticky routing for cache reuse.155For the gRPC API using the xAI SDK, pass `x-grok-conv-id` as gRPC metadata to enable sticky routing for cache reuse.

Details

17 17 

18The prompt prefix is identical to the previous request, with only a new user message appended:18The prompt prefix is identical to the previous request, with only a new user message appended:

19 19 

20```bash customLanguage="bash" addedLines="26"

21# Turn 1: Initial request (establishes the cache)

22curl https://api.x.ai/v1/chat/completions \

23 -H "Content-Type: application/json" \

24 -H "Authorization: Bearer $XAI_API_KEY" \

25 -H "x-grok-conv-id: conv_abc123" \

26 -d '{

27 "model": "grok-4.7",

28 "messages": [

29 {"role": "system", "content": "You are Grok, a helpful and truthful AI assistant built by xAI."},

30 {"role": "user", "content": "What is prompt caching?"},

31 {"role": "assistant", "content": "Prompt caching stores KV pairs from unchanged prompt prefixes so they can be reused on subsequent requests. This makes responses faster and cheaper."}

32 ]

33 }'

34 

35# Turn 2: Cache HIT — exact prefix preserved, new message appended

36curl https://api.x.ai/v1/chat/completions \

37 -H "Content-Type: application/json" \

38 -H "Authorization: Bearer $XAI_API_KEY" \

39 -H "x-grok-conv-id: conv_abc123" \

40 -d '{

41 "model": "grok-4.7",

42 "messages": [

43 {"role": "system", "content": "You are Grok, a helpful and truthful AI assistant built by xAI."},

44 {"role": "user", "content": "What is prompt caching?"},

45 {"role": "assistant", "content": "Prompt caching stores KV pairs from unchanged prompt prefixes so they can be reused on subsequent requests. This makes responses faster and cheaper."},

46 {"role": "user", "content": "Show me a code example."}

47 ]

48 }'

49```

50 

51```python customLanguage="pythonOpenAISDK"20```python customLanguage="pythonOpenAISDK"

52from openai import OpenAI21from openai import OpenAI

53 22 


83print(f"Turn 2 — Cached tokens: {response.usage.prompt_tokens_details.cached_tokens}")52print(f"Turn 2 — Cached tokens: {response.usage.prompt_tokens_details.cached_tokens}")

84```53```

85 54 

55```bash customLanguage="bash" addedLines="26"

56# Turn 1: Initial request (establishes the cache)

57curl https://api.x.ai/v1/chat/completions \

58 -H "Content-Type: application/json" \

59 -H "Authorization: Bearer $XAI_API_KEY" \

60 -H "x-grok-conv-id: conv_abc123" \

61 -d '{

62 "model": "grok-4.7",

63 "messages": [

64 {"role": "system", "content": "You are Grok, a helpful and truthful AI assistant built by xAI."},

65 {"role": "user", "content": "What is prompt caching?"},

66 {"role": "assistant", "content": "Prompt caching stores KV pairs from unchanged prompt prefixes so they can be reused on subsequent requests. This makes responses faster and cheaper."}

67 ]

68 }'

69 

70# Turn 2: Cache HIT — exact prefix preserved, new message appended

71curl https://api.x.ai/v1/chat/completions \

72 -H "Content-Type: application/json" \

73 -H "Authorization: Bearer $XAI_API_KEY" \

74 -H "x-grok-conv-id: conv_abc123" \

75 -d '{

76 "model": "grok-4.7",

77 "messages": [

78 {"role": "system", "content": "You are Grok, a helpful and truthful AI assistant built by xAI."},

79 {"role": "user", "content": "What is prompt caching?"},

80 {"role": "assistant", "content": "Prompt caching stores KV pairs from unchanged prompt prefixes so they can be reused on subsequent requests. This makes responses faster and cheaper."},

81 {"role": "user", "content": "Show me a code example."}

82 ]

83 }'

84```

85 

86```javascript customLanguage="javascriptOpenAISDK"86```javascript customLanguage="javascriptOpenAISDK"

87import OpenAI from 'openai';87import OpenAI from 'openai';

88 88 

Details

31console.log(text);31console.log(text);

32```32```

33 33 

34```python customLanguage="pythonXAI"

35import os

36 

37from xai_sdk import Client

38from xai_sdk.chat import user

39 

40client = Client(

41 api_key=os.getenv("XAI_API_KEY"),

42 api_host="us.api.x.ai",

43)

44 

45chat = client.chat.create(model="grok-4.7")

46chat.append(user("Explain latency versus throughput in two sentences."))

47 

48print(chat.sample().content)

49```

50 

51```python customLanguage="pythonOpenAISDK"34```python customLanguage="pythonOpenAISDK"

52import os35import os

53from openai import OpenAI36from openai import OpenAI


91 }'74 }'

92```75```

93 76 

77```python customLanguage="pythonXAI"

78import os

79 

80from xai_sdk import Client

81from xai_sdk.chat import user

82 

83client = Client(

84 api_key=os.getenv("XAI_API_KEY"),

85 api_host="us.api.x.ai",

86)

87 

88chat = client.chat.create(model="grok-4.7")

89chat.append(user("Explain latency versus throughput in two sentences."))

90 

91print(chat.sample().content)

92```

93 

94### Model availability94### Model availability

95 95 

96`grok-4.7` and `grok-4.6` are currently the only models available on the US endpoint; the [models page in the console](https://console.x.ai/team/default/models?cluster=us-central-1\&utm_source=docs\&utm_medium=referral\&utm_campaign=developers-advanced-api-usage-regions\&utm_content=models) and `GET https://us.api.x.ai/v1/models` always show the current list. Requesting a model that is not on that list, including `grok-latest`, fails with `404 Not Found`:96`grok-4.7` and `grok-4.6` are currently the only models available on the US endpoint; the [models page in the console](https://console.x.ai/team/default/models?cluster=us-central-1\&utm_source=docs\&utm_medium=referral\&utm_campaign=developers-advanced-api-usage-regions\&utm_content=models) and `GET https://us.api.x.ai/v1/models` always show the current list. Requesting a model that is not on that list, including `grok-latest`, fails with `404 Not Found`:

cost-tracking.md +81 −81

Details

57}57}

58```58```

59 59 

60```bash customLanguage="bash"

61curl https://api.x.ai/v1/responses \

62 -H "Authorization: Bearer $XAI_API_KEY" \

63 -H "Content-Type: application/json" \

64 -d '{

65 "model": "grok-4.7",

66 "input": "Say hello"

67 }' | jq '.usage.cost_in_usd_ticks'

68```

69 

70```python customLanguage="pythonOpenAISDK"60```python customLanguage="pythonOpenAISDK"

71import os61import os

72from openai import OpenAI62from openai import OpenAI


87print(f"Cost: ${cost_usd:.6f}")77print(f"Cost: ${cost_usd:.6f}")

88```78```

89 79 

80```bash customLanguage="bash"

81curl https://api.x.ai/v1/responses \

82 -H "Authorization: Bearer $XAI_API_KEY" \

83 -H "Content-Type: application/json" \

84 -d '{

85 "model": "grok-4.7",

86 "input": "Say hello"

87 }' | jq '.usage.cost_in_usd_ticks'

88```

89 

90```javascript customLanguage="javascriptOpenAISDK"90```javascript customLanguage="javascriptOpenAISDK"

91import OpenAI from "openai";91import OpenAI from "openai";

92 92 


115 115 

116When using the OpenAI SDK or the REST API, set `stream_options: { include_usage: true }` on the request. Cost is only included in the final chunk (with empty `choices`); intermediate chunks do not contain usage data.116When using the OpenAI SDK or the REST API, set `stream_options: { include_usage: true }` on the request. Cost is only included in the final chunk (with empty `choices`); intermediate chunks do not contain usage data.

117 117 

118```python customLanguage="pythonXAI"

119import os

120from xai_sdk import Client

121from xai_sdk.chat import user

122 

123client = Client(api_key=os.getenv("XAI_API_KEY"))

124 

125chat = client.chat.create(

126 model="grok-4.7",

127 messages=[user("Tell me a joke")],

128)

129 

130for response, chunk in chat.stream():

131 print(chunk.content, end="", flush=True)

132print()

133 

134# After the stream completes, cost is on the final response.

135print(f"Cost: ${response.cost_usd:.6f}")

136```

137 

138```python customLanguage="pythonOpenAISDK"118```python customLanguage="pythonOpenAISDK"

139import os119import os

140from openai import OpenAI120from openai import OpenAI


159 print(chunk.choices[0].delta.content or "", end="", flush=True)139 print(chunk.choices[0].delta.content or "", end="", flush=True)

160```140```

161 141 

162## Tracking cost across a conversation

163 

164`cost_in_usd_ticks` is per-request; it does not accumulate across turns. In a multi-turn conversation, sum the costs yourself:

165 

166```python customLanguage="pythonXAI"142```python customLanguage="pythonXAI"

167import os143import os

168from xai_sdk import Client144from xai_sdk import Client

169from xai_sdk.chat import system, user145from xai_sdk.chat import user

170 146 

171client = Client(api_key=os.getenv("XAI_API_KEY"))147client = Client(api_key=os.getenv("XAI_API_KEY"))

172 148 

173chat = client.chat.create(149chat = client.chat.create(

174 model="grok-4.7",150 model="grok-4.7",

175 messages=[system("You are a helpful assistant.")],151 messages=[user("Tell me a joke")],

176)152)

177 153 

178total_cost_usd = 0.0154for response, chunk in chat.stream():

179while True:155 print(chunk.content, end="", flush=True)

180 prompt = input("You: ")156print()

181 if prompt.lower() == "exit":

182 break

183 157 

184 chat.append(user(prompt))158# After the stream completes, cost is on the final response.

185 response = chat.sample()159print(f"Cost: ${response.cost_usd:.6f}")

186 print(f"Grok: {response.content}")160```

187 chat.append(response)

188 161 

189 total_cost_usd += response.cost_usd or 0.0162## Tracking cost across a conversation

190 print(f" (this turn: ${response.cost_usd or 0:.6f})")

191 163 

192print(f"Total session cost: ${total_cost_usd:.4f}")164`cost_in_usd_ticks` is per-request; it does not accumulate across turns. In a multi-turn conversation, sum the costs yourself:

193```

194 165 

195```python customLanguage="pythonOpenAISDK"166```python customLanguage="pythonOpenAISDK"

196import os167import os


227print(f"Total session cost: ${total_cost_usd:.4f}")198print(f"Total session cost: ${total_cost_usd:.4f}")

228```199```

229 200 

230## Server-side tools

231 

232When a request uses server-side tools (web search, X search, code execution), the model may make multiple internal calls before returning a final answer. The returned `cost_in_usd_ticks` covers all token costs and all tool invocations from that request in a single value. No separate accumulation needed.

233 

234```python customLanguage="pythonXAI"201```python customLanguage="pythonXAI"

235import os202import os

236from xai_sdk import Client203from xai_sdk import Client

237from xai_sdk.chat import user204from xai_sdk.chat import system, user

238from xai_sdk.tools import web_search, x_search

239 205 

240client = Client(api_key=os.getenv("XAI_API_KEY"))206client = Client(api_key=os.getenv("XAI_API_KEY"))

241 207 

242chat = client.chat.create(208chat = client.chat.create(

243 model="grok-4.7",209 model="grok-4.7",

244 tools=[web_search(), x_search()],210 messages=[system("You are a helpful assistant.")],

245)211)

246chat.append(user("What are people saying about xAI's latest announcement?"))

247 212 

248response = chat.sample()213total_cost_usd = 0.0

249print(response.content)214while True:

215 prompt = input("You: ")

216 if prompt.lower() == "exit":

217 break

250 218 

251# Shows which server-side tools were invoked and how many times.219 chat.append(user(prompt))

252print(f"Tools used: {response.server_side_tool_usage}")220 response = chat.sample()

253# Cost covers all model decodes + every tool call in the agentic loop.221 print(f"Grok: {response.content}")

254print(f"Cost: ${response.cost_usd:.4f}")222 chat.append(response)

223 

224 total_cost_usd += response.cost_usd or 0.0

225 print(f" (this turn: ${response.cost_usd or 0:.6f})")

226 

227print(f"Total session cost: ${total_cost_usd:.4f}")

255```228```

256 229 

230## Server-side tools

231 

232When a request uses server-side tools (web search, X search, code execution), the model may make multiple internal calls before returning a final answer. The returned `cost_in_usd_ticks` covers all token costs and all tool invocations from that request in a single value. No separate accumulation needed.

233 

257```python customLanguage="pythonOpenAISDK"234```python customLanguage="pythonOpenAISDK"

258import os235import os

259from openai import OpenAI236from openai import OpenAI


290 }' | jq '{tools_used: .usage.num_server_side_tools_used, cost_in_usd_ticks: .usage.cost_in_usd_ticks}'267 }' | jq '{tools_used: .usage.num_server_side_tools_used, cost_in_usd_ticks: .usage.cost_in_usd_ticks}'

291```268```

292 269 

270```python customLanguage="pythonXAI"

271import os

272from xai_sdk import Client

273from xai_sdk.chat import user

274from xai_sdk.tools import web_search, x_search

275 

276client = Client(api_key=os.getenv("XAI_API_KEY"))

277 

278chat = client.chat.create(

279 model="grok-4.7",

280 tools=[web_search(), x_search()],

281)

282chat.append(user("What are people saying about xAI's latest announcement?"))

283 

284response = chat.sample()

285print(response.content)

286 

287# Shows which server-side tools were invoked and how many times.

288print(f"Tools used: {response.server_side_tool_usage}")

289# Cost covers all model decodes + every tool call in the agentic loop.

290print(f"Cost: ${response.cost_usd:.4f}")

291```

292 

293## Image and video generation293## Image and video generation

294 294 

295Image and video responses include the same `cost_in_usd_ticks` field in their `usage` object:295Image and video responses include the same `cost_in_usd_ticks` field in their `usage` object:

296 296 

297```python customLanguage="pythonOpenAISDK"

298import os

299from openai import OpenAI

300 

301client = OpenAI(

302 api_key=os.getenv("XAI_API_KEY"),

303 base_url="https://api.x.ai/v1",

304)

305 

306response = client.images.generate(

307 model="grok-imagine-image-2.0",

308 prompt="A cat on a rocket",

309)

310 

311cost_ticks = response.usage.cost_in_usd_ticks

312print(f"Image cost: ${cost_ticks / 1e10:.4f}")

313```

314 

297```bash customLanguage="bash"315```bash customLanguage="bash"

298# Image generation316# Image generation

299curl https://api.x.ai/v1/images/generations \317curl https://api.x.ai/v1/images/generations \


327print(f"Video cost: ${video.cost_usd:.4f}")345print(f"Video cost: ${video.cost_usd:.4f}")

328```346```

329 347 

330```python customLanguage="pythonOpenAISDK"

331import os

332from openai import OpenAI

333 

334client = OpenAI(

335 api_key=os.getenv("XAI_API_KEY"),

336 base_url="https://api.x.ai/v1",

337)

338 

339response = client.images.generate(

340 model="grok-imagine-image-2.0",

341 prompt="A cat on a rocket",

342)

343 

344cost_ticks = response.usage.cost_in_usd_ticks

345print(f"Image cost: ${cost_ticks / 1e10:.4f}")

346```

347 

348## Batch API348## Batch API

349 349 

350Batch results include per-request costs. You can sum them to get the total batch cost, or read the `cost_breakdown` on the batch object itself. See [Batch API](/developers/advanced-api-usage/batch-api) for details.350Batch results include per-request costs. You can sum them to get the total batch cost, or read the `cost_breakdown` on the batch object itself. See [Batch API](/developers/advanced-api-usage/batch-api) for details.

faq/security.md +2 −2

Details

29 }'29 }'

30```30```

31 31 

32`safety_identifier` is accepted on [Chat Completions](/developers/rest-api-reference/inference/chat-completions), the [Responses API](/developers/rest-api-reference/inference/responses) (including streaming and [WebSocket mode](/developers/advanced-api-usage/websocket-mode)), [deferred chat completions](/developers/advanced-api-usage/deferred-chat-completions), the [Batch API](/developers/advanced-api-usage/batch-api), and the [gRPC `GetCompletionsRequest`](/developers/grpc-api-reference/chat). The legacy `user` field is still accepted for compatibility; `safety_identifier` is the documented field going forward.32`safety_identifier` is accepted on [Chat Completions](/developers/rest-api-reference/inference/chat-completions), the [Responses API](/developers/rest-api-reference/inference/responses) (including streaming and [WebSocket mode](/developers/advanced-api-usage/websocket-mode)), [deferred chat completions](/developers/advanced-api-usage/deferred-chat-completions), the [Batch API](/developers/advanced-api-usage/batch-api), and the gRPC `GetCompletionsRequest`. The legacy `user` field is still accepted for compatibility; `safety_identifier` is the documented field going forward.

33 33 

34If you run a gateway or platform that routes many end users through one API key (OpenRouter, Vercel AI Gateway, and similar), set `safety_identifier` per end user so an abuse report can be scoped to that user rather than to your key.34If you run a gateway or platform that routes many end users through one API key (OpenRouter, Vercel AI Gateway, and similar), set `safety_identifier` per end user so an abuse report can be scoped to that user rather than to your key.

35 35 


55| **[Collections API](/developers/files/collections)** | Stores your documents on SpaceXAI so Grok can search and retrieve from them (RAG). Those documents must be stored to be retrieved, which ZDR does not allow. Existing collections can still be viewed and deleted. |55| **[Collections API](/developers/files/collections)** | Stores your documents on SpaceXAI so Grok can search and retrieve from them (RAG). Those documents must be stored to be retrieved, which ZDR does not allow. Existing collections can still be viewed and deleted. |

56| **[Batch API](/developers/advanced-api-usage/batch-api)** | Processes large sets of requests asynchronously at a discounted price. Batched requests are stored until they are processed, so the Batch API is unavailable under ZDR. |56| **[Batch API](/developers/advanced-api-usage/batch-api)** | Processes large sets of requests asynchronously at a discounted price. Batched requests are stored until they are processed, so the Batch API is unavailable under ZDR. |

57| **[Deferred completions](/developers/advanced-api-usage/deferred-chat-completions)** | Return a request ID right away and let you fetch the result later instead of holding the connection open. That result is stored until you retrieve it, which ZDR does not permit. Concurrent client-side requests (for example via `AsyncClient`) are unaffected. |57| **[Deferred completions](/developers/advanced-api-usage/deferred-chat-completions)** | Return a request ID right away and let you fetch the result later instead of holding the connection open. That result is stored until you retrieve it, which ZDR does not permit. Concurrent client-side requests (for example via `AsyncClient`) are unaffected. |

58| **Stored [image](/developers/model-capabilities/images/generation) and [video](/developers/model-capabilities/video/generation) outputs** | Image and video generation can have SpaceXAI host the generated media and return it as a `file_id` or URL. Under ZDR there is no SpaceXAI-side output storage: no `file_id` inputs, no stored outputs, and no URL-format image results. For images, return base64 only. For video, you must supply your own `output.upload_url` (SpaceXAI uploads the result to your URL). Image generation is also limited to `grok-imagine` models and excludes agentic image generation. Video generation in the Console playground is unavailable for the same reason. |58| **Stored [image](/developers/model-capabilities/images/generation) and [video](/developers/model-capabilities/video/generation) outputs** | Image and video generation can have SpaceXAI host the generated media and return it as a `file_id` or URL. Under ZDR there is no SpaceXAI-side output storage: no `file_id` inputs and no stored outputs. For images, return base64 or supply your own pre-signed upload URLs in `output.upload_urls` (one per image, required for deferred image requests). For video, you must supply your own `output.upload_url`. In both cases SpaceXAI uploads the result to your URL via HTTP `PUT` and stores nothing itself. Image generation is also limited to `grok-imagine` models and excludes agentic image generation. The Console video playground asks for a pre-signed upload URL before each video it generates, plus an optional pre-signed view URL to watch the video. |

59| **Voice agent conversation history** | Voice agent conversations are not persisted when ZDR is enabled. |59| **Voice agent conversation history** | Voice agent conversations are not persisted when ZDR is enabled. |

60 60 

61### How to enable ZDR61### How to enable ZDR

files.md +32 −32

Details

61 61 

62Here's a quick example of the complete workflow:62Here's a quick example of the complete workflow:

63 63 

64```pythonXAI

65import os

66from xai_sdk import Client

67from xai_sdk.chat import user, file

68 

69client = Client(api_key=os.getenv("XAI_API_KEY"))

70 

71# 1a. Reference a public file by URL

72file_url = "https://example-files.online-convert.com/document/txt/example.txt"

73 

74# 1b. Or upload a file and reference by ID

75uploaded_file = client.files.upload(

76 b"Employee: Alice Johnson\\nDepartment: Engineering",

77 filename="employee.txt",

78)

79 

80# 2. Chat with files

81chat = client.chat.create(model="grok-4.7")

82chat.append(user(

83 "Summarize both documents",

84 file(url=file_url),

85 file(uploaded_file.id),

86))

87 

88# 3. Get the answer

89response = chat.sample()

90print(response.content)

91 

92# 4. Clean up uploaded file

93client.files.delete(uploaded_file.id)

94```

95 

96```javascriptWithoutSDK64```javascriptWithoutSDK

97// 1a. Reference a public file by URL65// 1a. Reference a public file by URL

98const fileUrl = "https://docs.x.ai/assets/api-examples/documents/sales-report.txt";66const fileUrl = "https://docs.x.ai/assets/api-examples/documents/sales-report.txt";


144});112});

145```113```

146 114 

115```pythonXAI

116import os

117from xai_sdk import Client

118from xai_sdk.chat import user, file

119 

120client = Client(api_key=os.getenv("XAI_API_KEY"))

121 

122# 1a. Reference a public file by URL

123file_url = "https://example-files.online-convert.com/document/txt/example.txt"

124 

125# 1b. Or upload a file and reference by ID

126uploaded_file = client.files.upload(

127 b"Employee: Alice Johnson\\nDepartment: Engineering",

128 filename="employee.txt",

129)

130 

131# 2. Chat with files

132chat = client.chat.create(model="grok-4.7")

133chat.append(user(

134 "Summarize both documents",

135 file(url=file_url),

136 file(uploaded_file.id),

137))

138 

139# 3. Get the answer

140response = chat.sample()

141print(response.content)

142 

143# 4. Clean up uploaded file

144client.files.delete(uploaded_file.id)

145```

146 

147## Key Features147## Key Features

148 148 

149### Multiple File Support149### Multiple File Support

Details

20 20 

21## Creating a collection21## Creating a collection

22 22 

23```python customLanguage="pythonXAI"

24import os

25from xai_sdk import Client

26client = Client(

27 api_key=os.getenv("XAI_API_KEY"),

28 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

29 timeout=3600,

30)

31 

32collection = client.collections.create(

33 name="SEC Filings",

34)

35 

36print(collection)

37```

38 

39```javascript customLanguage="javascriptWithoutSDK"23```javascript customLanguage="javascriptWithoutSDK"

40const response = await fetch('https://management-api.x.ai/v1/collections', {24const response = await fetch('https://management-api.x.ai/v1/collections', {

41 method: 'POST',25 method: 'POST',


58 -d '{"collection_name": "SEC Filings"}'42 -d '{"collection_name": "SEC Filings"}'

59```43```

60 44 

61## Listing collections

62 

63```python customLanguage="pythonXAI"45```python customLanguage="pythonXAI"

64# ... Create client46import os

65collections = client.collections.list()47from xai_sdk import Client

66print(collections)48client = Client(

49 api_key=os.getenv("XAI_API_KEY"),

50 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

51 timeout=3600,

52)

53 

54collection = client.collections.create(

55 name="SEC Filings",

56)

57 

58print(collection)

67```59```

68 60 

61## Listing collections

62 

69```javascript customLanguage="javascriptWithoutSDK"63```javascript customLanguage="javascriptWithoutSDK"

70const response = await fetch('https://management-api.x.ai/v1/collections', {64const response = await fetch('https://management-api.x.ai/v1/collections', {

71 headers: {65 headers: {


82 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"76 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"

83```77```

84 78 

85## Viewing collection configuration

86 

87```python customLanguage="pythonXAI"79```python customLanguage="pythonXAI"

88# ... Create client80# ... Create client

89collection = client.collections.get("collection_dbc087b1-6c99-493d-86c6-b401fee34a9d")81collections = client.collections.list()

90 82print(collections)

91print(collection)

92```83```

93 84 

85## Viewing collection configuration

86 

94```javascript customLanguage="javascriptWithoutSDK"87```javascript customLanguage="javascriptWithoutSDK"

95const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';88const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';

96const response = await fetch(`https://management-api.x.ai/v1/collections/${collectionId}`, {89const response = await fetch(`https://management-api.x.ai/v1/collections/${collectionId}`, {


108 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"101 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"

109```102```

110 103 

111## Updating collection configuration

112 

113```python customLanguage="pythonXAI"104```python customLanguage="pythonXAI"

114# ... Create client105# ... Create client

115collection = client.collections.update(106collection = client.collections.get("collection_dbc087b1-6c99-493d-86c6-b401fee34a9d")

116 "collection_dbc087b1-6c99-493d-86c6-b401fee34a9d",

117 name="SEC Filings (New)"

118)

119 107 

120print(collection)108print(collection)

121```109```

122 110 

111## Updating collection configuration

112 

123```javascript customLanguage="javascriptWithoutSDK"113```javascript customLanguage="javascriptWithoutSDK"

124const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';114const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';

125const response = await fetch(`https://management-api.x.ai/v1/collections/${collectionId}`, {115const response = await fetch(`https://management-api.x.ai/v1/collections/${collectionId}`, {


143 -d '{"collection_name": "SEC Filings (New)"}'133 -d '{"collection_name": "SEC Filings (New)"}'

144```134```

145 135 

136```python customLanguage="pythonXAI"

137# ... Create client

138collection = client.collections.update(

139 "collection_dbc087b1-6c99-493d-86c6-b401fee34a9d",

140 name="SEC Filings (New)"

141)

142 

143print(collection)

144```

145 

146## Uploading documents146## Uploading documents

147 147 

148Uploading a document to a collection is a two-step process:148Uploading a document to a collection is a two-step process:


1501. Upload the file to the xAI API1501. Upload the file to the xAI API

1512. Add the uploaded file to your collection1512. Add the uploaded file to your collection

152 152 

153```python customLanguage="pythonXAI"

154# ... Create client

155with open("tesla-20241231.html", "rb") as file:

156 file_data = file.read()

157 

158document = client.collections.upload_document(

159 collection_id="collection_dbc087b1-6c99-493d-86c6-b401fee34a9d",

160 name="tesla-20241231.html",

161 data=file_data,

162)

163print(document)

164```

165 

166```javascript customLanguage="javascriptWithoutSDK"153```javascript customLanguage="javascriptWithoutSDK"

167import fs from 'fs';154import fs from 'fs';

168 155 


200 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"187 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"

201```188```

202 189 

190```python customLanguage="pythonXAI"

191# ... Create client

192with open("tesla-20241231.html", "rb") as file:

193 file_data = file.read()

194 

195document = client.collections.upload_document(

196 collection_id="collection_dbc087b1-6c99-493d-86c6-b401fee34a9d",

197 name="tesla-20241231.html",

198 data=file_data,

199)

200print(document)

201```

202 

203### Uploading with metadata fields203### Uploading with metadata fields

204 204 

205If your collection has [metadata fields](/developers/files/collections/metadata) defined (the collection must have these fields set in `field_definitions` when created or updated - see the linked metadata page for details), include them using the `fields` parameter:205If your collection has [metadata fields](/developers/files/collections/metadata) defined (the collection must have these fields set in `field_definitions` when created or updated - see the linked metadata page for details), include them using the `fields` parameter:

206 206 

207```bash

208curl https://management-api.x.ai/v1/collections/collection_dbc087b1-6c99-493d-86c6-b401fee34a9d/documents \

209 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY" \

210 -F "name=paper.pdf" \

211 -F "data=@paper.pdf" \

212 -F "content_type=application/pdf" \

213 -F 'fields={"author": "Sandra Kim", "year": "2024", "title": "Q3 Revenue Analysis"}'

214```

215 

207```python customLanguage="pythonXAI"216```python customLanguage="pythonXAI"

208# ... Create client217# ... Create client

209with open("paper.pdf", "rb") as file:218with open("paper.pdf", "rb") as file:


222print(document)231print(document)

223```232```

224 233 

225```bash

226curl https://management-api.x.ai/v1/collections/collection_dbc087b1-6c99-493d-86c6-b401fee34a9d/documents \

227 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY" \

228 -F "name=paper.pdf" \

229 -F "data=@paper.pdf" \

230 -F "content_type=application/pdf" \

231 -F 'fields={"author": "Sandra Kim", "year": "2024", "title": "Q3 Revenue Analysis"}'

232```

233 

234## Searching documents234## Searching documents

235 235 

236You can also search documents using the Responses API with the `file_search` tool. See the [Collections Search Tool](/developers/tools/collections-search) guide for more details.236You can also search documents using the Responses API with the `file_search` tool. See the [Collections Search Tool](/developers/tools/collections-search) guide for more details.

237 237 

238```python customLanguage="pythonXAI"

239# ... Create client

240response = client.collections.search(

241 query="What were the key revenue drivers based on the SEC filings?",

242 collection_ids=["collection_dbc087b1-6c99-493d-86c6-b401fee34a9d"],

243)

244print(response)

245```

246 

247```javascript customLanguage="javascriptWithoutSDK"238```javascript customLanguage="javascriptWithoutSDK"

248const response = await fetch('https://api.x.ai/v1/documents/search', {239const response = await fetch('https://api.x.ai/v1/documents/search', {

249 method: 'POST',240 method: 'POST',


275 }'266 }'

276```267```

277 268 

269```python customLanguage="pythonXAI"

270# ... Create client

271response = client.collections.search(

272 query="What were the key revenue drivers based on the SEC filings?",

273 collection_ids=["collection_dbc087b1-6c99-493d-86c6-b401fee34a9d"],

274)

275print(response)

276```

277 

278### Search modes278### Search modes

279 279 

280There are three search methods available:280There are three search methods available:


314 314 

315## Deleting a document315## Deleting a document

316 316 

317```python customLanguage="pythonXAI"

318# ... Create client

319 

320client.collections.remove_document(

321 collection_id="collection_dbc087b1-6c99-493d-86c6-b401fee34a9d",

322 file_id="file_55a709d4-8edc-4f83-84d9-9f04fe49f832",

323)

324```

325 

326```javascript customLanguage="javascriptWithoutSDK"317```javascript customLanguage="javascriptWithoutSDK"

327const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';318const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';

328const fileId = 'file_55a709d4-8edc-4f83-84d9-9f04fe49f832';319const fileId = 'file_55a709d4-8edc-4f83-84d9-9f04fe49f832';


340 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"331 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"

341```332```

342 333 

343## Deleting a collection

344 

345```python customLanguage="pythonXAI"334```python customLanguage="pythonXAI"

346# ... Create client335# ... Create client

347 336 

348client.collections.delete(collection_id="collection_dbc087b1-6c99-493d-86c6-b401fee34a9d")337client.collections.remove_document(

338 collection_id="collection_dbc087b1-6c99-493d-86c6-b401fee34a9d",

339 file_id="file_55a709d4-8edc-4f83-84d9-9f04fe49f832",

340)

349```341```

350 342 

343## Deleting a collection

344 

351```javascript customLanguage="javascriptWithoutSDK"345```javascript customLanguage="javascriptWithoutSDK"

352const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';346const collectionId = 'collection_dbc087b1-6c99-493d-86c6-b401fee34a9d';

353 347 


364 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"358 -H "Authorization: Bearer $XAI_MANAGEMENT_API_KEY"

365```359```

366 360 

361```python customLanguage="pythonXAI"

362# ... Create client

363 

364client.collections.delete(collection_id="collection_dbc087b1-6c99-493d-86c6-b401fee34a9d")

365```

366 

367## Next Steps367## Next Steps

Details

12 12 

13### Upload from File Path13### Upload from File Path

14 14 

15```pythonXAI

16import os

17from xai_sdk import Client

18 

19client = Client(api_key=os.getenv("XAI_API_KEY"))

20 

21# Upload a file from disk

22file = client.files.upload("/path/to/your/document.pdf")

23 

24print(f"File ID: {file.id}")

25print(f"Filename: {file.filename}")

26print(f"Size: {file.size} bytes")

27print(f"Created at: {file.created_at}")

28```

29 

30```pythonOpenAISDK15```pythonOpenAISDK

31import os16import os

32from openai import OpenAI17from openai import OpenAI


110 -F purpose=assistants95 -F purpose=assistants

111```96```

112 97 

98```pythonXAI

99import os

100from xai_sdk import Client

101 

102client = Client(api_key=os.getenv("XAI_API_KEY"))

103 

104# Upload a file from disk

105file = client.files.upload("/path/to/your/document.pdf")

106 

107print(f"File ID: {file.id}")

108print(f"Filename: {file.filename}")

109print(f"Size: {file.size} bytes")

110print(f"Created at: {file.created_at}")

111```

112 

113### Upload from Bytes113### Upload from Bytes

114 114 

115```pythonXAI115```pythonXAI


157>157>

158> **Multipart field ordering matters**: `expires_after` must appear **before** the `file` field in the multipart body. Requests that send `expires_after` after `file` are rejected with `400`.158> **Multipart field ordering matters**: `expires_after` must appear **before** the `file` field in the multipart body. Requests that send `expires_after` after `file` are rejected with `400`.

159 159 

160```pythonXAI

161import os

162from datetime import timedelta

163from xai_sdk import Client

164 

165client = Client(api_key=os.getenv("XAI_API_KEY"))

166 

167# Upload a file that will be auto-deleted in 24 hours.

168# expires_after accepts an int (seconds) or a datetime.timedelta.

169file = client.files.upload(

170 "/path/to/document.pdf",

171 expires_after=timedelta(hours=24),

172)

173 

174print(f"File ID: {file.id}")

175print(f"Expires at: {file.expires_at.ToDatetime()}")

176```

177 

178```pythonOpenAISDK160```pythonOpenAISDK

179import os161import os

180from openai import OpenAI162from openai import OpenAI


275 -F file=@/path/to/document.pdf257 -F file=@/path/to/document.pdf

276```258```

277 259 

260```pythonXAI

261import os

262from datetime import timedelta

263from xai_sdk import Client

264 

265client = Client(api_key=os.getenv("XAI_API_KEY"))

266 

267# Upload a file that will be auto-deleted in 24 hours.

268# expires_after accepts an int (seconds) or a datetime.timedelta.

269file = client.files.upload(

270 "/path/to/document.pdf",

271 expires_after=timedelta(hours=24),

272)

273 

274print(f"File ID: {file.id}")

275print(f"Expires at: {file.expires_at.ToDatetime()}")

276```

277 

278## Upload with Progress Tracking278## Upload with Progress Tracking

279 279 

280Track upload progress for large files using callbacks or progress bars.280Track upload progress for large files using callbacks or progress bars.


314 314 

315When more files remain, the response includes a `pagination_token`. On the last page it is `null` or empty.315When more files remain, the response includes a `pagination_token`. On the last page it is `null` or empty.

316 316 

317```pythonXAI

318import os

319from xai_sdk import Client

320 

321client = Client(api_key=os.getenv("XAI_API_KEY"))

322 

323# List files with pagination and sorting

324response = client.files.list(

325 limit=10,

326 order="desc",

327 sort_by="created_at"

328)

329 

330for file in response.data:

331 expires = file.expires_at.ToDatetime() if file.HasField("expires_at") else "never"

332 print(f"File: {file.filename} (ID: {file.id}, Size: {file.size} bytes, Expires: {expires})")

333```

334 

335```pythonOpenAISDK317```pythonOpenAISDK

336import os318import os

337from openai import OpenAI319from openai import OpenAI


397 -H "Authorization: Bearer $XAI_API_KEY"379 -H "Authorization: Bearer $XAI_API_KEY"

398```380```

399 381 

400### Paginating Through All Files

401 

402The List endpoint returns at most `limit` files per call (capped at 100). To enumerate every file, keep calling the endpoint with the `pagination_token` from the previous response until the response no longer includes one.

403 

404```pythonXAI382```pythonXAI

405import os383import os

406from xai_sdk import Client384from xai_sdk import Client

407 385 

408client = Client(api_key=os.getenv("XAI_API_KEY"))386client = Client(api_key=os.getenv("XAI_API_KEY"))

409 387 

410# Walk every page until the API stops returning a pagination token.388# List files with pagination and sorting

411page_size = 100389response = client.files.list(

412token = None390 limit=10,

413all_files = []

414 

415while True:

416 response = client.files.list(

417 limit=page_size,

418 order="desc",391 order="desc",

419 sort_by="created_at",392 sort_by="created_at"

420 pagination_token=token,393)

421 )

422 all_files.extend(response.data)

423 token = response.pagination_token

424 if not token:

425 break

426 394 

427print(f"Total files: {len(all_files)}")395for file in response.data:

396 expires = file.expires_at.ToDatetime() if file.HasField("expires_at") else "never"

397 print(f"File: {file.filename} (ID: {file.id}, Size: {file.size} bytes, Expires: {expires})")

428```398```

429 399 

400### Paginating Through All Files

401 

402The List endpoint returns at most `limit` files per call (capped at 100). To enumerate every file, keep calling the endpoint with the `pagination_token` from the previous response until the response no longer includes one.

403 

430```pythonRequests404```pythonRequests

431import os405import os

432import requests406import requests


468console.log(\`Total files: \${allFiles.length}\`);442console.log(\`Total files: \${allFiles.length}\`);

469```443```

470 444 

471## Getting File Metadata

472 

473Retrieve detailed information about a specific file.

474 

475```pythonXAI445```pythonXAI

476import os446import os

477from xai_sdk import Client447from xai_sdk import Client

478 448 

479client = Client(api_key=os.getenv("XAI_API_KEY"))449client = Client(api_key=os.getenv("XAI_API_KEY"))

480 450 

481# Get file metadata by ID451# Walk every page until the API stops returning a pagination token.

482file = client.files.get("file-abc123")452page_size = 100

453token = None

454all_files = []

483 455 

484print(f"Filename: {file.filename}")456while True:

485print(f"Size: {file.size} bytes")457 response = client.files.list(

486print(f"Created: {file.created_at}")458 limit=page_size,

487# expires_at is only set when the file was uploaded with expires_after459 order="desc",

488if file.HasField("expires_at"):460 sort_by="created_at",

489 print(f"Expires at: {file.expires_at.ToDatetime()}")461 pagination_token=token,

462 )

463 all_files.extend(response.data)

464 token = response.pagination_token

465 if not token:

466 break

467 

468print(f"Total files: {len(all_files)}")

490```469```

491 470 

471## Getting File Metadata

472 

473Retrieve detailed information about a specific file.

474 

492```pythonOpenAISDK475```pythonOpenAISDK

493import os476import os

494from openai import OpenAI477from openai import OpenAI


554 -H "Authorization: Bearer $XAI_API_KEY"537 -H "Authorization: Bearer $XAI_API_KEY"

555```538```

556 539 

557## Getting File Content

558 

559Download the raw bytes of an uploaded file. The endpoint streams the response, so it works for files of any supported size without buffering the whole payload in memory at the API layer.

560 

561```pythonXAI540```pythonXAI

562import os541import os

563from xai_sdk import Client542from xai_sdk import Client

564 543 

565client = Client(api_key=os.getenv("XAI_API_KEY"))544client = Client(api_key=os.getenv("XAI_API_KEY"))

566 545 

567# Returns the complete file content as bytes.546# Get file metadata by ID

568content = client.files.content("file-abc123")547file = client.files.get("file-abc123")

569 

570# Save to disk

571with open("downloaded.pdf", "wb") as f:

572 f.write(content)

573 548 

574print(f"Saved {len(content)} bytes")549print(f"Filename: {file.filename}")

550print(f"Size: {file.size} bytes")

551print(f"Created: {file.created_at}")

552# expires_at is only set when the file was uploaded with expires_after

553if file.HasField("expires_at"):

554 print(f"Expires at: {file.expires_at.ToDatetime()}")

575```555```

576 556 

557## Getting File Content

558 

559Download the raw bytes of an uploaded file. The endpoint streams the response, so it works for files of any supported size without buffering the whole payload in memory at the API layer.

560 

577```pythonOpenAISDK561```pythonOpenAISDK

578import os562import os

579from openai import OpenAI563from openai import OpenAI


634 --output downloaded.pdf618 --output downloaded.pdf

635```619```

636 620 

637## Deleting Files

638 

639Remove files when they're no longer needed.

640 

641```pythonXAI621```pythonXAI

642import os622import os

643from xai_sdk import Client623from xai_sdk import Client

644 624 

645client = Client(api_key=os.getenv("XAI_API_KEY"))625client = Client(api_key=os.getenv("XAI_API_KEY"))

646 626 

647# Delete a file627# Returns the complete file content as bytes.

648delete_response = client.files.delete("file-abc123")628content = client.files.content("file-abc123")

649 629 

650print(f"Deleted: {delete_response.deleted}")630# Save to disk

651print(f"File ID: {delete_response.id}")631with open("downloaded.pdf", "wb") as f:

632 f.write(content)

633 

634print(f"Saved {len(content)} bytes")

652```635```

653 636 

637## Deleting Files

638 

639Remove files when they're no longer needed.

640 

654```pythonOpenAISDK641```pythonOpenAISDK

655import os642import os

656from openai import OpenAI643from openai import OpenAI


715 -H "Authorization: Bearer $XAI_API_KEY"702 -H "Authorization: Bearer $XAI_API_KEY"

716```703```

717 704 

705```pythonXAI

706import os

707from xai_sdk import Client

708 

709client = Client(api_key=os.getenv("XAI_API_KEY"))

710 

711# Delete a file

712delete_response = client.files.delete("file-abc123")

713 

714print(f"Deleted: {delete_response.deleted}")

715print(f"File ID: {delete_response.id}")

716```

717 

718## The File Object718## The File Object

719 719 

720Every Files API endpoint that returns metadata (Upload, List, Get Metadata) returns the same `file` object shape:720Every Files API endpoint that returns metadata (Upload, List, Get Metadata) returns the same `file` object shape:

Details

22 22 

23## Quick Start23## Quick Start

24 24 

25```pythonXAI

26import os

27from xai_sdk import Client

28 

29client = Client(api_key=os.getenv("XAI_API_KEY"))

30 

31# 1. Upload (or reference an existing) file

32file = client.files.upload("/path/to/diagram.png")

33 

34# 2. Create the public URL

35resp = client.files.create_public_url(file.id)

36 

37print(resp.public_url)

38# https://files-cdn.x.ai/<token>/file_abc123.png

39 

40# 3. When you're done sharing, revoke it

41client.files.revoke_public_url(file.id)

42```

43 

44```bash25```bash

45# 1. Upload (or reference an existing) file26# 1. Upload (or reference an existing) file

46FILE_ID=$(curl -s https://api.x.ai/v1/files \\27FILE_ID=$(curl -s https://api.x.ai/v1/files \\


60 -H "Authorization: Bearer $XAI_API_KEY"41 -H "Authorization: Bearer $XAI_API_KEY"

61```42```

62 43 

44```pythonXAI

45import os

46from xai_sdk import Client

47 

48client = Client(api_key=os.getenv("XAI_API_KEY"))

49 

50# 1. Upload (or reference an existing) file

51file = client.files.upload("/path/to/diagram.png")

52 

53# 2. Create the public URL

54resp = client.files.create_public_url(file.id)

55 

56print(resp.public_url)

57# https://files-cdn.x.ai/<token>/file_abc123.png

58 

59# 3. When you're done sharing, revoke it

60client.files.revoke_public_url(file.id)

61```

62 

63> [!WARNING]63> [!WARNING]

64>64>

65> Public URLs can only be created for files that **already exist** in your Files API storage. You65> Public URLs can only be created for files that **already exist** in your Files API storage. You


79 79 

80`expires_after` must be between **3600 seconds (1 hour)** and **2592000 seconds (30 days)**. A public URL can never outlive its file — requesting an `expires_after` greater than the file's remaining lifetime is rejected.80`expires_after` must be between **3600 seconds (1 hour)** and **2592000 seconds (30 days)**. A public URL can never outlive its file — requesting an `expires_after` greater than the file's remaining lifetime is rejected.

81 81 

82```pythonXAI

83import os

84from datetime import timedelta

85from xai_sdk import Client

86 

87client = Client(api_key=os.getenv("XAI_API_KEY"))

88file = client.files.upload("/path/to/photo.png")

89 

90# 1. Indefinite: omit expires_after on a file with no expiry.

91# Must call revoke_public_url to explicitly revoke the public URL.

92resp = client.files.create_public_url(file.id)

93assert not resp.HasField("expires_at")

94 

95# 2. URL-bound: pass expires_after as int seconds or a timedelta

96resp = client.files.create_public_url(file.id, expires_after=timedelta(hours=24))

97print(f"Expires at: {resp.expires_at.seconds}")

98 

99# 3. Inherited: file has its own expiration, omit expires_after on the URL

100ttl_file = client.files.upload(

101 b"\\x89PNG\\r\\n\\x1a\\n" + b"\\x00" * 32,

102 filename="short-lived.png",

103 expires_after=timedelta(hours=2),

104)

105resp = client.files.create_public_url(ttl_file.id)

106# resp.expires_at matches the file's expires_at

107```

108 

109```bash82```bash

110# 1. Indefinite — file has no expiry.83# 1. Indefinite — file has no expiry.

111# Must call POST /public-url/revoke to explicitly revoke.84# Must call POST /public-url/revoke to explicitly revoke.


134# {"public_url":"...","expires_at":<matches file expiry>}107# {"public_url":"...","expires_at":<matches file expiry>}

135```108```

136 109 

110```pythonXAI

111import os

112from datetime import timedelta

113from xai_sdk import Client

114 

115client = Client(api_key=os.getenv("XAI_API_KEY"))

116file = client.files.upload("/path/to/photo.png")

117 

118# 1. Indefinite: omit expires_after on a file with no expiry.

119# Must call revoke_public_url to explicitly revoke the public URL.

120resp = client.files.create_public_url(file.id)

121assert not resp.HasField("expires_at")

122 

123# 2. URL-bound: pass expires_after as int seconds or a timedelta

124resp = client.files.create_public_url(file.id, expires_after=timedelta(hours=24))

125print(f"Expires at: {resp.expires_at.seconds}")

126 

127# 3. Inherited: file has its own expiration, omit expires_after on the URL

128ttl_file = client.files.upload(

129 b"\\x89PNG\\r\\n\\x1a\\n" + b"\\x00" * 32,

130 filename="short-lived.png",

131 expires_after=timedelta(hours=2),

132)

133resp = client.files.create_public_url(ttl_file.id)

134# resp.expires_at matches the file's expires_at

135```

136 

137## Idempotency137## Idempotency

138 138 

139A file can have **at most one active public URL at a time**. Calling `create_public_url` on a file that already has one returns the existing URL without producing a new one — it's safe to call repeatedly.139A file can have **at most one active public URL at a time**. Calling `create_public_url` on a file that already has one returns the existing URL without producing a new one — it's safe to call repeatedly.


165 165 

166Revoking invalidates the URL and clears it from the file's metadata. The original file is untouched and continues to be accessible through authenticated endpoints.166Revoking invalidates the URL and clears it from the file's metadata. The original file is untouched and continues to be accessible through authenticated endpoints.

167 167 

168```bash

169curl -s -X POST "https://api.x.ai/v1/files/file_abc123/public-url/revoke" \\

170 -H "Authorization: Bearer $XAI_API_KEY"

171# {"id":"file_abc123","revoked":true,"public_url":"https://files-cdn.x.ai/..."}

172 

173# Calling again is safe — returns revoked=false

174curl -s -X POST "https://api.x.ai/v1/files/file_abc123/public-url/revoke" \\

175 -H "Authorization: Bearer $XAI_API_KEY"

176# {"id":"file_abc123","revoked":false}

177```

178 

168```pythonXAI179```pythonXAI

169import os180import os

170from xai_sdk import Client181from xai_sdk import Client


187client.files.revoke_public_url("file_abc123") # no-op, no error198client.files.revoke_public_url("file_abc123") # no-op, no error

188```199```

189 200 

190```bash

191curl -s -X POST "https://api.x.ai/v1/files/file_abc123/public-url/revoke" \\

192 -H "Authorization: Bearer $XAI_API_KEY"

193# {"id":"file_abc123","revoked":true,"public_url":"https://files-cdn.x.ai/..."}

194 

195# Calling again is safe — returns revoked=false

196curl -s -X POST "https://api.x.ai/v1/files/file_abc123/public-url/revoke" \\

197 -H "Authorization: Bearer $XAI_API_KEY"

198# {"id":"file_abc123","revoked":false}

199```

200 

201**Revocation is all-or-nothing.** A file can only have one public URL at a time, so revoking breaks the link for everyone who has it. If a link leaks to the wrong party, the only remedy is to revoke and create a new URL — the new one will have a fresh token and the old URL stays permanently dead.201**Revocation is all-or-nothing.** A file can only have one public URL at a time, so revoking breaks the link for everyone who has it. If a link leaks to the wrong party, the only remedy is to revoke and create a new URL — the new one will have a fresh token and the old URL stays permanently dead.

202 202 

203## Finding Files with a Public URL203## Finding Files with a Public URL


206 206 

207You can also use the [`filter`](/developers/rest-api-reference/files/manage) parameter on `list_files` to find files with or without an active public URL:207You can also use the [`filter`](/developers/rest-api-reference/files/manage) parameter on `list_files` to find files with or without an active public URL:

208 208 

209```bash

210# URL-encode the filter

211curl -s "https://api.x.ai/v1/files?filter=public_url%20!%3D%20null" \\

212 -H "Authorization: Bearer $XAI_API_KEY"

213```

214 

209```pythonXAI215```pythonXAI

210import os216import os

211from xai_sdk import Client217from xai_sdk import Client


221without_url = client.files.list(filter="public_url = null")227without_url = client.files.list(filter="public_url = null")

222```228```

223 229 

224```bash

225# URL-encode the filter

226curl -s "https://api.x.ai/v1/files?filter=public_url%20!%3D%20null" \\

227 -H "Authorization: Bearer $XAI_API_KEY"

228```

229 

230## Limitations230## Limitations

231 231 

232* **Maximum file size: 50 MiB.** Larger files remain available through the authenticated Files API but cannot be made public.232* **Maximum file size: 50 MiB.** Larger files remain available through the authenticated Files API but cannot be made public.

grok-4-7.md +14 −14

Details

8 8 

9If you already have an [API key](https://console.x.ai/team/default/api-keys?utm_source=docs\&utm_medium=referral\&utm_campaign=developers-grok-4-7\&utm_content=api-keys), set the model name to `grok-4.7`:9If you already have an [API key](https://console.x.ai/team/default/api-keys?utm_source=docs\&utm_medium=referral\&utm_campaign=developers-grok-4-7\&utm_content=api-keys), set the model name to `grok-4.7`:

10 10 

11```python customLanguage="pythonXAI"

12import os

13from xai_sdk import Client

14from xai_sdk.chat import user

15 

16client = Client(api_key=os.getenv("XAI_API_KEY"))

17 

18chat = client.chat.create(model="grok-4.7")

19chat.append(user("Find and fix the bug, then explain it: function median(a){a.sort();return a[a.length/2]}"))

20 

21response = chat.sample()

22print(response.content)

23```

24 

25```javascript customLanguage="javascriptAISDK"11```javascript customLanguage="javascriptAISDK"

26import { xai } from '@ai-sdk/xai';12import { xai } from '@ai-sdk/xai';

27import { generateText } from 'ai';13import { generateText } from 'ai';


67 }'53 }'

68```54```

69 55 

56```python customLanguage="pythonXAI"

57import os

58from xai_sdk import Client

59from xai_sdk.chat import user

60 

61client = Client(api_key=os.getenv("XAI_API_KEY"))

62 

63chat = client.chat.create(model="grok-4.7")

64chat.append(user("Find and fix the bug, then explain it: function median(a){a.sort();return a[a.length/2]}"))

65 

66response = chat.sample()

67print(response.content)

68```

69 

70New to the xAI API? Follow the [Quickstart](/developers/quickstart) to create an account and make your first request.70New to the xAI API? Follow the [Quickstart](/developers/quickstart) to create an account and make your first request.

71 71 

72## At a glance72## At a glance

grpc-api-reference.md +0 −29 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Overview

4 

5The xAI gRPC API exposes the same models and services as the REST API over gRPC. The base URL for all services is `api.x.ai`, and every call must carry the header `Authorization: Bearer <your xAI API key>`.

6 

7The protobuf definitions are published in [xai-org/xai-proto](https://github.com/xai-org/xai-proto). The [xAI Python SDK](https://github.com/xai-org/xai-sdk-python) (`xai-sdk`) uses gRPC natively; install it with `pip install xai-sdk`.

8 

9## Using buf curl

10 

11Clone the proto definitions and use [buf curl](https://buf.build/docs/curl/usage) to call the API:

12 

13```bash

14git clone https://github.com/xai-org/xai-proto.git

15cd xai-proto

16```

17 

18All `buf curl` examples in this reference assume you run from inside the cloned `xai-proto` directory.

19 

20## Services

21 

22* [Chat](/developers/grpc-api-reference/chat) — `xai_api.Chat`

23* [Image](/developers/grpc-api-reference/image) — `xai_api.Image`

24* [Video](/developers/grpc-api-reference/video) — `xai_api.Video`

25* [Batch Management](/developers/grpc-api-reference/batches) — `xai_api.BatchMgmt`

26* [Models](/developers/grpc-api-reference/models) — `xai_api.Models`

27* [Auth](/developers/grpc-api-reference/auth) — `xai_api.Auth`

28* [Tokenize](/developers/grpc-api-reference/tokenize) — `xai_api.Tokenize`

29* [Raw Sampling](/developers/grpc-api-reference/sample) — `xai_api.Sample`

grpc-api-reference/auth.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Auth

grpc-api-reference/batches.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Batch Management

grpc-api-reference/chat.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Chat

grpc-api-reference/image.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Image

grpc-api-reference/models.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Models

grpc-api-reference/sample.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Raw Sampling

grpc-api-reference/tokenize.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Tokenize

grpc-api-reference/video.md +0 −3 deleted

File Deleted View Diff

1#### gRPC API

2 

3# Video

Details

37* `medium` spends more compute on each image for finer detail; choose it where output quality matters more than cost.37* `medium` spends more compute on each image for finer detail; choose it where output quality matters more than cost.

38* `auto` (the default when `quality` is omitted) currently serves `low` for generation and `medium` for editing, and you are billed at the quality served.38* `auto` (the default when `quality` is omitted) currently serves `low` for generation and `medium` for editing, and you are billed at the quality served.

39 39 

40```python customLanguage="pythonXAI"

41import xai_sdk

42 

43client = xai_sdk.Client()

44 

45response = client.image.sample(

46 prompt="A watercolor painting of a lighthouse at dawn",

47 model="grok-imagine-image-2.0",

48 quality="low",

49)

50 

51print(response.url)

52```

53 

54```python customLanguage="pythonOpenAISDK"40```python customLanguage="pythonOpenAISDK"

55from openai import OpenAI41from openai import OpenAI

56 42 


96 }'82 }'

97```83```

98 84 

85```python customLanguage="pythonXAI"

86import xai_sdk

87 

88client = xai_sdk.Client()

89 

90response = client.image.sample(

91 prompt="A watercolor painting of a lighthouse at dawn",

92 model="grok-imagine-image-2.0",

93 quality="low",

94)

95 

96print(response.url)

97```

98 

99The same `model` change applies to [image editing](/developers/model-capabilities/images/editing) requests on `/v1/images/edits`.99The same `model` change applies to [image editing](/developers/model-capabilities/images/editing) requests on `/v1/images/edits`.

100 100 

101## Need help?101## Need help?

Details

26 26 

27Attach a file to a conversation to let the model search through it for relevant information.27Attach a file to a conversation to let the model search through it for relevant information.

28 28 

29```pythonXAI

30import os

31from xai_sdk import Client

32from xai_sdk.chat import user, file

33 

34client = Client(api_key=os.getenv("XAI_API_KEY"))

35 

36# Attach a file by public URL (or use file(file_id) for uploaded files)

37chat = client.chat.create(model="grok-4.7")

38chat.append(user(

39 "What was the total revenue in this report?",

40 file(url="https://docs.x.ai/assets/api-examples/documents/sales-report.txt"),

41))

42 

43# Get the response

44response = chat.sample()

45 

46print(f"Answer: {response.content}")

47```

48 

49```pythonOpenAISDK29```pythonOpenAISDK

50import os30import os

51from openai import OpenAI31from openai import OpenAI


146 }'126 }'

147```127```

148 128 

149## Streaming Chat with Files

150 

151Get real-time responses while the model searches through your documents.

152 

153```pythonXAI129```pythonXAI

154import os130import os

155from xai_sdk import Client131from xai_sdk import Client


160# Attach a file by public URL (or use file(file_id) for uploaded files)136# Attach a file by public URL (or use file(file_id) for uploaded files)

161chat = client.chat.create(model="grok-4.7")137chat = client.chat.create(model="grok-4.7")

162chat.append(user(138chat.append(user(

163 "What is the weight of the XR-2000?",139 "What was the total revenue in this report?",

164 file(url="https://docs.x.ai/assets/api-examples/documents/product-specs.txt"),140 file(url="https://docs.x.ai/assets/api-examples/documents/sales-report.txt"),

165))141))

166 142 

167# Stream the response143# Get the response

168is_thinking = True144response = chat.sample()

169for response, chunk in chat.stream():

170 # Show tool calls as they happen

171 for tool_call in chunk.tool_calls:

172 print(f"\\nSearching: {tool_call.function.name}")

173

174 if response.usage.reasoning_tokens and is_thinking:

175 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

176 145 

177 if chunk.content and is_thinking:146print(f"Answer: {response.content}")

178 print("\\n\\nAnswer:")147```

179 is_thinking = False

180 148 

181 if chunk.content:149## Streaming Chat with Files

182 print(chunk.content, end="", flush=True)

183 150 

184print(f"\\n\\nUsage: {response.usage}")151Get real-time responses while the model searches through your documents.

185```

186 152 

187```javascriptOpenAISDK153```javascriptOpenAISDK

188import OpenAI from "openai";154import OpenAI from "openai";


216console.log();182console.log();

217```183```

218 184 

219## Multiple File Attachments

220 

221Query across multiple documents simultaneously.

222 

223```pythonXAI185```pythonXAI

224import os186import os

225from xai_sdk import Client187from xai_sdk import Client


227 189 

228client = Client(api_key=os.getenv("XAI_API_KEY"))190client = Client(api_key=os.getenv("XAI_API_KEY"))

229 191 

230# Attach files by public URL (or use file(file_id) for uploaded files)192# Attach a file by public URL (or use file(file_id) for uploaded files)

231chat = client.chat.create(model="grok-4.7")193chat = client.chat.create(model="grok-4.7")

232chat.append(194chat.append(user(

233 user(195 "What is the weight of the XR-2000?",

234 "Based on these documents, when did the project start, what is the budget, and how many people are on the team?",196 file(url="https://docs.x.ai/assets/api-examples/documents/product-specs.txt"),

235 file(url="https://docs.x.ai/assets/api-examples/documents/project-timeline.txt"),197))

236 file(url="https://docs.x.ai/assets/api-examples/documents/project-budget.txt"),

237 file(url="https://docs.x.ai/assets/api-examples/documents/project-team.txt"),

238 )

239)

240 198 

241response = chat.sample()199# Stream the response

200is_thinking = True

201for response, chunk in chat.stream():

202 # Show tool calls as they happen

203 for tool_call in chunk.tool_calls:

204 print(f"\\nSearching: {tool_call.function.name}")

242 205

243print(f"Answer: {response.content}")206 if response.usage.reasoning_tokens and is_thinking:

244print("\\nDocuments searched: 3")207 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

208

209 if chunk.content and is_thinking:

210 print("\\n\\nAnswer:")

211 is_thinking = False

212

213 if chunk.content:

214 print(chunk.content, end="", flush=True)

215 

216print(f"\\n\\nUsage: {response.usage}")

245```217```

246 218 

219## Multiple File Attachments

220 

221Query across multiple documents simultaneously.

222 

247```javascriptOpenAISDK223```javascriptOpenAISDK

248import OpenAI from "openai";224import OpenAI from "openai";

249 225 


276console.log("Documents searched: 3");252console.log("Documents searched: 3");

277```253```

278 254 

279## Multi-Turn Conversations with Files

280 

281Maintain context across multiple questions about the same documents. Use encrypted content to preserve file context efficiently across multiple turns.

282 

283```pythonXAI255```pythonXAI

284import os256import os

285from xai_sdk import Client257from xai_sdk import Client


287 259 

288client = Client(api_key=os.getenv("XAI_API_KEY"))260client = Client(api_key=os.getenv("XAI_API_KEY"))

289 261 

290# Create a multi-turn conversation with encrypted content262# Attach files by public URL (or use file(file_id) for uploaded files)

291chat = client.chat.create(263chat = client.chat.create(model="grok-4.7")

292 model="grok-4.7",264chat.append(

293 use_encrypted_content=True, # Enable encrypted content for efficient multi-turn265 user(

266 "Based on these documents, when did the project start, what is the budget, and how many people are on the team?",

267 file(url="https://docs.x.ai/assets/api-examples/documents/project-timeline.txt"),

268 file(url="https://docs.x.ai/assets/api-examples/documents/project-budget.txt"),

269 file(url="https://docs.x.ai/assets/api-examples/documents/project-team.txt"),

270 )

294)271)

295 272 

296# First turn: Attach a file by public URL (or use file(file_id) for uploaded files)273response = chat.sample()

297chat.append(user(

298 "What is the employee's name?",

299 file(url="https://docs.x.ai/assets/api-examples/documents/employee-info.txt"),

300))

301response1 = chat.sample()

302print("Q1: What is the employee's name?")

303print(f"A1: {response1.content}\\n")

304 

305# Add the response to conversation history

306chat.append(response1)

307 274 

308# Second turn: Ask about department (agentic context is retained via encrypted content)275print(f"Answer: {response.content}")

309chat.append(user("What department does this employee work in?"))276print("\\nDocuments searched: 3")

310response2 = chat.sample()277```

311print("Q2: What department does this employee work in?")

312print(f"A2: {response2.content}\\n")

313 278 

314# Add the response to conversation history279## Multi-Turn Conversations with Files

315chat.append(response2)

316 280 

317# Third turn: Ask about skills281Maintain context across multiple questions about the same documents. Use encrypted content to preserve file context efficiently across multiple turns.

318chat.append(user("What skills does this employee have?"))

319response3 = chat.sample()

320print("Q3: What skills does this employee have?")

321print(f"A3: {response3.content}\\n")

322```

323 282 

324```javascriptOpenAISDK283```javascriptOpenAISDK

325import OpenAI from "openai";284import OpenAI from "openai";


373console.log("A3: " + response3.output[response3.output.length - 1].content[0].text + "\\n");332console.log("A3: " + response3.output[response3.output.length - 1].content[0].text + "\\n");

374```333```

375 334 

376## Combining Files with Other Modalities

377 

378You can combine file attachments with images and other content types in a single message.

379 

380```pythonXAI335```pythonXAI

381import os336import os

382from xai_sdk import Client337from xai_sdk import Client

383from xai_sdk.chat import user, file, image338from xai_sdk.chat import user, file

384 339 

385client = Client(api_key=os.getenv("XAI_API_KEY"))340client = Client(api_key=os.getenv("XAI_API_KEY"))

386 341 

387# Attach files by public URL (or use file(file_id) for uploaded files)342# Create a multi-turn conversation with encrypted content

388chat = client.chat.create(model="grok-4.7")343chat = client.chat.create(

389chat.append(344 model="grok-4.7",

390 user(345 use_encrypted_content=True, # Enable encrypted content for efficient multi-turn

391 "Based on the attached care guide, do you have any advice about the pictured cat?",

392 file(url="https://docs.x.ai/assets/api-examples/documents/cat-care.txt"),

393 image("https://media.x.ai/v1/docs/example-cat-in-tree-8e9ac3e0.png"),

394 )

395)346)

396 347 

397response = chat.sample()348# First turn: Attach a file by public URL (or use file(file_id) for uploaded files)

349chat.append(user(

350 "What is the employee's name?",

351 file(url="https://docs.x.ai/assets/api-examples/documents/employee-info.txt"),

352))

353response1 = chat.sample()

354print("Q1: What is the employee's name?")

355print(f"A1: {response1.content}\\n")

398 356 

399print(f"Analysis: {response.content}")357# Add the response to conversation history

358chat.append(response1)

359 

360# Second turn: Ask about department (agentic context is retained via encrypted content)

361chat.append(user("What department does this employee work in?"))

362response2 = chat.sample()

363print("Q2: What department does this employee work in?")

364print(f"A2: {response2.content}\\n")

365 

366# Add the response to conversation history

367chat.append(response2)

368 

369# Third turn: Ask about skills

370chat.append(user("What skills does this employee have?"))

371response3 = chat.sample()

372print("Q3: What skills does this employee have?")

373print(f"A3: {response3.content}\\n")

400```374```

401 375 

376## Combining Files with Other Modalities

377 

378You can combine file attachments with images and other content types in a single message.

379 

402```javascriptOpenAISDK380```javascriptOpenAISDK

403import OpenAI from "openai";381import OpenAI from "openai";

404 382 


432console.log("Analysis: " + analysis);410console.log("Analysis: " + analysis);

433```411```

434 412 

435## Combining Files with Code Execution

436 

437For data analysis tasks, you can attach data files and enable the code execution tool. This allows Grok to write and run Python code to analyze and process your data.

438 

439```pythonXAI413```pythonXAI

440import os414import os

441from xai_sdk import Client415from xai_sdk import Client

442from xai_sdk.chat import user, file416from xai_sdk.chat import user, file, image

443from xai_sdk.tools import code_execution

444 417 

445client = Client(api_key=os.getenv("XAI_API_KEY"))418client = Client(api_key=os.getenv("XAI_API_KEY"))

446 419 

447# Attach a file by public URL (or use file(file_id) for uploaded files)420# Attach files by public URL (or use file(file_id) for uploaded files)

448chat = client.chat.create(421chat = client.chat.create(model="grok-4.7")

449 model="grok-4.7",

450 tools=[code_execution()], # Enable code execution

451)

452 

453chat.append(422chat.append(

454 user(423 user(

455 "Analyze this sales data and calculate: 1) Total revenue by product, 2) Average units sold by region, 3) Which product-region combination has the highest revenue",424 "Based on the attached care guide, do you have any advice about the pictured cat?",

456 file(url="https://docs.x.ai/assets/api-examples/documents/sales-data.csv"),425 file(url="https://docs.x.ai/assets/api-examples/documents/cat-care.txt"),

426 image("https://media.x.ai/v1/docs/example-cat-in-tree-8e9ac3e0.png"),

457 )427 )

458)428)

459 429 

460# Stream the response to see code execution in real-time430response = chat.sample()

461is_thinking = True

462for response, chunk in chat.stream():

463 for tool_call in chunk.tool_calls:

464 if tool_call.function.name == "code_execution":

465 print("\\n[Executing Code]")

466

467 if response.usage.reasoning_tokens and is_thinking:

468 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

469 431 

470 if chunk.content and is_thinking:432print(f"Analysis: {response.content}")

471 print("\\n\\nAnalysis Results:")433```

472 is_thinking = False

473 434 

474 if chunk.content:435## Combining Files with Code Execution

475 print(chunk.content, end="", flush=True)

476 436 

477print(f"\\n\\nUsage: {response.usage}")437For data analysis tasks, you can attach data files and enable the code execution tool. This allows Grok to write and run Python code to analyze and process your data.

478```

479 438 

480```javascriptOpenAISDK439```javascriptOpenAISDK

481import OpenAI from "openai";440import OpenAI from "openai";


515console.log();474console.log();

516```475```

517 476 

477```pythonXAI

478import os

479from xai_sdk import Client

480from xai_sdk.chat import user, file

481from xai_sdk.tools import code_execution

482 

483client = Client(api_key=os.getenv("XAI_API_KEY"))

484 

485# Attach a file by public URL (or use file(file_id) for uploaded files)

486chat = client.chat.create(

487 model="grok-4.7",

488 tools=[code_execution()], # Enable code execution

489)

490 

491chat.append(

492 user(

493 "Analyze this sales data and calculate: 1) Total revenue by product, 2) Average units sold by region, 3) Which product-region combination has the highest revenue",

494 file(url="https://docs.x.ai/assets/api-examples/documents/sales-data.csv"),

495 )

496)

497 

498# Stream the response to see code execution in real-time

499is_thinking = True

500for response, chunk in chat.stream():

501 for tool_call in chunk.tool_calls:

502 if tool_call.function.name == "code_execution":

503 print("\\n[Executing Code]")

504

505 if response.usage.reasoning_tokens and is_thinking:

506 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

507

508 if chunk.content and is_thinking:

509 print("\\n\\nAnalysis Results:")

510 is_thinking = False

511

512 if chunk.content:

513 print(chunk.content, end="", flush=True)

514 

515print(f"\\n\\nUsage: {response.usage}")

516```

517 

518The model will:518The model will:

519 519 

5201. Access the attached data file5201. Access the attached data file

Details

10 10 

11With the xAI SDK, use the same `sample()` method; just add the `image_url` parameter:11With the xAI SDK, use the same `sample()` method; just add the `image_url` parameter:

12 12 

13```python customLanguage="pythonXAI"13```javascript customLanguage="javascriptAISDK"

14import base6414import { xai } from "@ai-sdk/xai";

15import xai_sdk15import { generateImage } from "ai";

16 16import fs from "fs";

17client = xai_sdk.Client()

18 17 

19# Load image from file and encode as base6418// Load image and encode as base64

20with open("photo.png", "rb") as f:19const imageBuffer = fs.readFileSync("photo.png");

21 image_data = base64.b64encode(f.read()).decode("utf-8")20const base64Image = imageBuffer.toString("base64");

22 21 

23response = client.image.sample(22const { image } = await generateImage({

24 prompt="Render this as a pencil sketch with detailed shading",23 model: xai.image("grok-imagine-image-2.0"),

25 model="grok-imagine-image-2.0",24 prompt: {

26 image_url=f"data:image/png;base64,{image_data}",25 text: "Render this as a pencil sketch with detailed shading",

27)26 images: [`data:image/png;base64,${base64Image}`],

27 },

28});

28 29 

29print(response.url)30console.log(image.base64);

30```31```

31 32 

32```bash33```bash


44 }'45 }'

45```46```

46 47 

47```javascript customLanguage="javascriptAISDK"48```python customLanguage="pythonXAI"

48import { xai } from "@ai-sdk/xai";49import base64

49import { generateImage } from "ai";50import xai_sdk

50import fs from "fs";

51 51 

52// Load image and encode as base6452client = xai_sdk.Client()

53const imageBuffer = fs.readFileSync("photo.png");

54const base64Image = imageBuffer.toString("base64");

55 53 

56const { image } = await generateImage({54# Load image from file and encode as base64

57 model: xai.image("grok-imagine-image-2.0"),55with open("photo.png", "rb") as f:

58 prompt: {56 image_data = base64.b64encode(f.read()).decode("utf-8")

59 text: "Render this as a pencil sketch with detailed shading",

60 images: [`data:image/png;base64,${base64Image}`],

61 },

62});

63 57 

64console.log(image.base64);58response = client.image.sample(

59 prompt="Render this as a pencil sketch with detailed shading",

60 model="grok-imagine-image-2.0",

61 image_url=f"data:image/png;base64,{image_data}",

62)

63 

64print(response.url)

65```65```

66 66 

67You can provide the source image as:67You can provide the source image as:

Details

8 8 

9Generate an image with a single API call:9Generate an image with a single API call:

10 10 

11```python customLanguage="pythonXAI"11```javascript customLanguage="javascriptAISDK"

12import xai_sdk12import { xai } from "@ai-sdk/xai";

13 13import { generateImage } from "ai";

14client = xai_sdk.Client()

15 

16response = client.image.sample(

17 prompt="A collage of London landmarks in a stenciled street‑art style",

18 model="grok-imagine-image-2.0",

19)

20 14 

21print(response.url)15const { image } = await generateImage({

22```16 model: xai.image("grok-imagine-image-2.0"),

17 prompt: "A collage of London landmarks in a stenciled street‑art style",

18});

23 19 

24```bash20console.log(image.base64);

25curl -X POST https://api.x.ai/v1/images/generations \

26 -H "Content-Type: application/json" \

27 -H "Authorization: Bearer $XAI_API_KEY" \

28 -d '{

29 "model": "grok-imagine-image-2.0",

30 "prompt": "A collage of London landmarks in a stenciled street‑art style"

31 }'

32```21```

33 22 

34```python customLanguage="pythonOpenAISDK"23```python customLanguage="pythonOpenAISDK"


47print(response.data[0].url)36print(response.data[0].url)

48```37```

49 38 

39```bash

40curl -X POST https://api.x.ai/v1/images/generations \

41 -H "Content-Type: application/json" \

42 -H "Authorization: Bearer $XAI_API_KEY" \

43 -d '{

44 "model": "grok-imagine-image-2.0",

45 "prompt": "A collage of London landmarks in a stenciled street‑art style"

46 }'

47```

48 

50```javascript customLanguage="javascriptOpenAISDK"49```javascript customLanguage="javascriptOpenAISDK"

51import OpenAI from "openai";50import OpenAI from "openai";

52 51 


63console.log(response.data[0].url);62console.log(response.data[0].url);

64```63```

65 64 

66```javascript customLanguage="javascriptAISDK"65```python customLanguage="pythonXAI"

67import { xai } from "@ai-sdk/xai";66import xai_sdk

68import { generateImage } from "ai";

69 67 

70const { image } = await generateImage({68client = xai_sdk.Client()

71 model: xai.image("grok-imagine-image-2.0"),

72 prompt: "A collage of London landmarks in a stenciled street‑art style",

73});

74 69 

75console.log(image.base64);70response = client.image.sample(

71 prompt="A collage of London landmarks in a stenciled street‑art style",

72 model="grok-imagine-image-2.0",

73)

74 

75print(response.url)

76```76```

77 77 

78Images are returned as URLs by default. URLs are temporary, so download or process promptly. You can also request [base64 output](#base64-output) for embedding images directly.78Images are returned as URLs by default. URLs are temporary, so download or process promptly. You can also request [base64 output](#base64-output) for embedding images directly.


83 83 

84Generate multiple images in a single request with the `n` parameter (`1`–`10`). On the REST API and OpenAI-compatible SDKs, `n` is optional and defaults to `1`. The xAI Python SDK uses `sample()` for a single image and `sample_batch(n=...)` for more than one — `n` is required on `sample_batch()`.84Generate multiple images in a single request with the `n` parameter (`1`–`10`). On the REST API and OpenAI-compatible SDKs, `n` is optional and defaults to `1`. The xAI Python SDK uses `sample()` for a single image and `sample_batch(n=...)` for more than one — `n` is required on `sample_batch()`.

85 85 

86```python customLanguage="pythonXAI"86```javascript customLanguage="javascriptAISDK"

87import xai_sdk87import { xai } from "@ai-sdk/xai";

88import { generateImage } from "ai";

88 89 

89client = xai_sdk.Client()90const { images } = await generateImage({

91 model: xai.image("grok-imagine-image-2.0"),

92 prompt: "A futuristic city skyline at night",

93 n: 4,

94});

90 95 

91responses = client.image.sample_batch(96images.forEach((image, i) => {

92 prompt="A futuristic city skyline at night",97 console.log(`Variation ${i + 1}: ${image.base64.slice(0, 50)}...`);

93 model="grok-imagine-image-2.0",98});

94 n=4,

95)

96 99 

97for i, image in enumerate(responses):

98 print(f"Variation {i + 1}: {image.url}")

99```100```

100 101 

101```python customLanguage="pythonOpenAISDK"102```python customLanguage="pythonOpenAISDK"


136 137 

137```138```

138 139 

139```javascript customLanguage="javascriptAISDK"

140import { xai } from "@ai-sdk/xai";

141import { generateImage } from "ai";

142 

143const { images } = await generateImage({

144 model: xai.image("grok-imagine-image-2.0"),

145 prompt: "A futuristic city skyline at night",

146 n: 4,

147});

148 

149images.forEach((image, i) => {

150 console.log(`Variation ${i + 1}: ${image.base64.slice(0, 50)}...`);

151});

152 

153```

154 

155```bash140```bash

156curl -X POST https://api.x.ai/v1/images/generations \141curl -X POST https://api.x.ai/v1/images/generations \

157 -H "Content-Type: application/json" \142 -H "Content-Type: application/json" \


163 }'148 }'

164```149```

165 150 

151```python customLanguage="pythonXAI"

152import xai_sdk

153 

154client = xai_sdk.Client()

155 

156responses = client.image.sample_batch(

157 prompt="A futuristic city skyline at night",

158 model="grok-imagine-image-2.0",

159 n=4,

160)

161 

162for i, image in enumerate(responses):

163 print(f"Variation {i + 1}: {image.url}")

164```

165 

166### Aspect Ratio166### Aspect Ratio

167 167 

168Control image dimensions with the `aspect_ratio` parameter. When omitted, the default is `auto`, which lets the model pick the best ratio for the prompt.168Control image dimensions with the `aspect_ratio` parameter. When omitted, the default is `auto`, which lets the model pick the best ratio for the prompt.


180| `5:2` | Wide banners |180| `5:2` | Wide banners |

181| `auto` | Model auto-selects the best ratio for the prompt |181| `auto` | Model auto-selects the best ratio for the prompt |

182 182 

183```python customLanguage="pythonXAI"183```javascript customLanguage="javascriptAISDK"

184import xai_sdk184import { xai } from "@ai-sdk/xai";

185 185import { generateImage } from "ai";

186client = xai_sdk.Client()

187 186 

188response = client.image.sample(187const { image } = await generateImage({

189 prompt="Mountain landscape at sunrise",188 model: xai.image("grok-imagine-image-2.0"),

190 model="grok-imagine-image-2.0",189 prompt: "Mountain landscape at sunrise",

191 aspect_ratio="16:9",190 aspectRatio: "16:9",

192)191});

193 192 

194print(response.url)193console.log(image.base64);

195```194```

196 195 

197```python customLanguage="pythonOpenAISDK"196```python customLanguage="pythonOpenAISDK"


229console.log(response.data[0].url);228console.log(response.data[0].url);

230```229```

231 230 

232```javascript customLanguage="javascriptAISDK"

233import { xai } from "@ai-sdk/xai";

234import { generateImage } from "ai";

235 

236const { image } = await generateImage({

237 model: xai.image("grok-imagine-image-2.0"),

238 prompt: "Mountain landscape at sunrise",

239 aspectRatio: "16:9",

240});

241 

242console.log(image.base64);

243```

244 

245```bash231```bash

246curl -X POST https://api.x.ai/v1/images/generations \232curl -X POST https://api.x.ai/v1/images/generations \

247 -H "Content-Type: application/json" \233 -H "Content-Type: application/json" \


253 }'239 }'

254```240```

255 241 

256### Resolution

257 

258You can specify different resolutions of the output image with the `resolution` parameter. Currently supported image resolutions are:

259 

260* `1k` (default when omitted)

261* `2k`

262 

263```python customLanguage="pythonXAI"242```python customLanguage="pythonXAI"

264import xai_sdk243import xai_sdk

265 244 

266client = xai_sdk.Client()245client = xai_sdk.Client()

267 246 

268response = client.image.sample(247response = client.image.sample(

269 prompt="An astronaut performing EVA in LEO.",248 prompt="Mountain landscape at sunrise",

270 model="grok-imagine-image-2.0",249 model="grok-imagine-image-2.0",

271 resolution="2k"250 aspect_ratio="16:9",

272)251)

273 252 

274print(response.url)253print(response.url)

275```254```

276 255 

256### Resolution

257 

258You can specify different resolutions of the output image with the `resolution` parameter. Currently supported image resolutions are:

259 

260* `1k` (default when omitted)

261* `2k`

262 

263```javascript customLanguage="javascriptAISDK"

264import { xai } from "@ai-sdk/xai";

265import { generateImage } from "ai";

266 

267const { image } = await generateImage({

268 model: xai.image("grok-imagine-image-2.0"),

269 prompt: "An astronaut performing EVA in LEO.",

270 providerOptions: {

271 xai: { resolution: "2k" },

272 },

273});

274 

275console.log(image.base64);

276```

277 

277```python customLanguage="pythonOpenAISDK"278```python customLanguage="pythonOpenAISDK"

278from openai import OpenAI279from openai import OpenAI

279 280 


309console.log(response.data[0].url);310console.log(response.data[0].url);

310```311```

311 312 

312```javascript customLanguage="javascriptAISDK"

313import { xai } from "@ai-sdk/xai";

314import { generateImage } from "ai";

315 

316const { image } = await generateImage({

317 model: xai.image("grok-imagine-image-2.0"),

318 prompt: "An astronaut performing EVA in LEO.",

319 providerOptions: {

320 xai: { resolution: "2k" },

321 },

322});

323 

324console.log(image.base64);

325```

326 

327```bash313```bash

328curl -X POST https://api.x.ai/v1/images/generations \314curl -X POST https://api.x.ai/v1/images/generations \

329-H "Content-Type: application/json" \315-H "Content-Type: application/json" \


335}'321}'

336```322```

337 323 

338### Quality

339 

340Control generation quality with the optional `quality` parameter. Allowed values are `low`, `medium`, and `auto`. When omitted, the default is `auto`, which lets the service choose the quality for each request. Auto currently uses `low` for image generation and `medium` for [image editing](/developers/model-capabilities/images/editing). Images are billed at the quality they are served at (see [Pricing](/developers/pricing)). Pass `low` or `medium` to pin a specific quality. The parameter is only supported for `grok-imagine-image-2.0`.

341 

342```python customLanguage="pythonXAI"324```python customLanguage="pythonXAI"

343import xai_sdk325import xai_sdk

344 326 

345client = xai_sdk.Client()327client = xai_sdk.Client()

346 328 

347response = client.image.sample(329response = client.image.sample(

348 prompt="A watercolor painting of a lighthouse at dawn",330 prompt="An astronaut performing EVA in LEO.",

349 model="grok-imagine-image-2.0",331 model="grok-imagine-image-2.0",

350 quality="low",332 resolution="2k"

351)333)

352 334 

353print(response.url)335print(response.url)

354```336```

355 337 

338### Quality

339 

340Control generation quality with the optional `quality` parameter. Allowed values are `low`, `medium`, and `auto`. When omitted, the default is `auto`, which lets the service choose the quality for each request. Auto currently uses `low` for image generation and `medium` for [image editing](/developers/model-capabilities/images/editing). Images are billed at the quality they are served at (see [Pricing](/developers/pricing)). Pass `low` or `medium` to pin a specific quality. The parameter is only supported for `grok-imagine-image-2.0`.

341 

356```bash342```bash

357curl -X POST https://api.x.ai/v1/images/generations \343curl -X POST https://api.x.ai/v1/images/generations \

358 -H "Content-Type: application/json" \344 -H "Content-Type: application/json" \


364 }'350 }'

365```351```

366 352 

367### Base64 Output

368 

369Control the output format with the `response_format` parameter. When omitted, the default is `url`, which returns temporary hosted URLs. For embedding images directly without downloading, request base64:

370 

371```python customLanguage="pythonXAI"353```python customLanguage="pythonXAI"

372import xai_sdk354import xai_sdk

373 355 

374client = xai_sdk.Client()356client = xai_sdk.Client()

375 357 

376response = client.image.sample(358response = client.image.sample(

377 prompt="A serene Japanese garden",359 prompt="A watercolor painting of a lighthouse at dawn",

378 model="grok-imagine-image-2.0",360 model="grok-imagine-image-2.0",

379 image_format="base64",361 quality="low",

380)362)

381 363 

382# Save to file364print(response.url)

383with open("garden.jpg", "wb") as f:365```

384 f.write(response.image)366 

367### Base64 Output

368 

369Control the output format with the `response_format` parameter. When omitted, the default is `url`, which returns temporary hosted URLs. For embedding images directly without downloading, request base64:

370 

371```javascript customLanguage="javascriptAISDK"

372import { xai } from "@ai-sdk/xai";

373import { generateImage } from "ai";

374import fs from "fs";

375 

376const { image } = await generateImage({

377 model: xai.image("grok-imagine-image-2.0"),

378 prompt: "A serene Japanese garden",

379});

380 

381// Save to file (AI SDK returns base64 by default)

382const imageBuffer = Buffer.from(image.base64, "base64");

383fs.writeFileSync("garden.jpg", imageBuffer);

385```384```

386 385 

387```python customLanguage="pythonOpenAISDK"386```python customLanguage="pythonOpenAISDK"


425fs.writeFileSync("garden.jpg", imageBuffer);424fs.writeFileSync("garden.jpg", imageBuffer);

426```425```

427 426 

428```javascript customLanguage="javascriptAISDK"427```bash

429import { xai } from "@ai-sdk/xai";428curl -X POST https://api.x.ai/v1/images/generations \

430import { generateImage } from "ai";429 -H "Content-Type: application/json" \

431import fs from "fs";430 -H "Authorization: Bearer $XAI_API_KEY" \

431 -d '{

432 "model": "grok-imagine-image-2.0",

433 "prompt": "A serene Japanese garden",

434 "response_format": "b64_json"

435 }'

436```

432 437 

433const { image } = await generateImage({438```python customLanguage="pythonXAI"

434 model: xai.image("grok-imagine-image-2.0"),439import xai_sdk

435 prompt: "A serene Japanese garden",

436});

437 440 

438// Save to file (AI SDK returns base64 by default)441client = xai_sdk.Client()

439const imageBuffer = Buffer.from(image.base64, "base64");442 

440fs.writeFileSync("garden.jpg", imageBuffer);443response = client.image.sample(

444 prompt="A serene Japanese garden",

445 model="grok-imagine-image-2.0",

446 image_format="base64",

447)

448 

449# Save to file

450with open("garden.jpg", "wb") as f:

451 f.write(response.image)

452```

453 

454### Deferred Generation

455 

456Image generation normally completes within the request. For long-running batches, or to avoid holding a connection open, set `deferred: true` on `/v1/images/generations` or `/v1/images/edits`. The request returns a `request_id` immediately; poll `GET /v1/images/{request_id}` for the result. Deferred requests support `response_format: "url"` only (the default). Results are stored alongside [deferred chat completions](/developers/advanced-api-usage/deferred-chat-completions) and retained for 24 hours.

457 

458The poll response carries a `status`:

459 

460* `pending` (HTTP `202`): still generating

461* `done` (HTTP `200`): `data` and `usage` are populated, in the same shape as a synchronous response

462* `failed` (HTTP `200`): `error` holds a `code` and `message`

463 

464```python customLanguage="pythonRequests"

465import os

466import time

467import requests

468 

469headers = {

470 "Authorization": f"Bearer {os.getenv('XAI_API_KEY')}",

471 "Content-Type": "application/json",

472}

473 

474# Step 1: Start generation

475response = requests.post(

476 "https://api.x.ai/v1/images/generations",

477 headers=headers,

478 json={

479 "model": "grok-imagine-image-2.0",

480 "prompt": "A serene Japanese garden",

481 "deferred": True,

482 },

483)

484request_id = response.json()["request_id"]

485 

486# Step 2: Poll for the result

487while True:

488 result = requests.get(

489 f"https://api.x.ai/v1/images/{request_id}",

490 headers={"Authorization": headers["Authorization"]},

491 )

492 data = result.json()

493 

494 if data["status"] == "done":

495 print(f"Image URL: {data['data'][0]['url']}")

496 break

497 elif data["status"] == "failed":

498 print(f"Generation failed: {data['error']['message']}")

499 break

500 else:

501 print("Still processing...")

502 time.sleep(5)

441```503```

442 504 

443```bash505```bash

506# Step 1: Start generation

444curl -X POST https://api.x.ai/v1/images/generations \507curl -X POST https://api.x.ai/v1/images/generations \

445 -H "Content-Type: application/json" \508 -H "Content-Type: application/json" \

446 -H "Authorization: Bearer $XAI_API_KEY" \509 -H "Authorization: Bearer $XAI_API_KEY" \

447 -d '{510 -d '{

448 "model": "grok-imagine-image-2.0",511 "model": "grok-imagine-image-2.0",

449 "prompt": "A serene Japanese garden",512 "prompt": "A serene Japanese garden",

450 "response_format": "b64_json"513 "deferred": true

451 }'514 }'

515# => {"request_id": "..."}

516 

517# Step 2: Poll for the result

518curl https://api.x.ai/v1/images/<request_id> \

519 -H "Authorization: Bearer $XAI_API_KEY"

520# => 202 {"request_id": "...", "status": "pending"}

521# => 200 {"request_id": "...", "status": "done", "data": [{"url": "...", "mime_type": "image/jpeg"}], "usage": {...}}

452```522```

453 523 

524You can also register a [webhook](/developers/rest-api-reference/management/webhooks) for the `image.generation.completed`, `image.generation.failed`, `image.edit.completed` and `image.edit.failed` events instead of polling.

525 

454### Response Details526### Response Details

455 527 

456The xAI SDK exposes additional metadata on the response object beyond the image URL or base64 data.528The xAI SDK exposes additional metadata on the response object beyond the image URL or base64 data.

Details

46 46 

47### Image understanding example47### Image understanding example

48 48 

49```python customLanguage="pythonXAI"49```javascript customLanguage="javascriptAISDK"

50import os50import { xai } from '@ai-sdk/xai';

51from xai_sdk import Client51import { generateText } from 'ai';

52from xai_sdk.chat import user, image

53 

54client = Client(

55 api_key=os.getenv("XAI_API_KEY"),

56 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

57 timeout=3600,

58)

59 

60image_url = "https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png"

61chat = client.chat.create(model="grok-4.7")

62chat.append(

63 user(

64 "What's in this image?",

65 image(image_url=image_url, detail="high"),

66 )

67)

68 52 

69response = chat.sample()53const { text, response } = await generateText({

70print(response)54 model: xai.responses('grok-4.7'),

55 messages: [

56 {

57 role: 'user',

58 content: [

59 {

60 type: 'image',

61 image: new URL('https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png'),

62 },

63 {

64 type: 'text',

65 text: "What's in this image?",

66 },

67 ],

68 },

69 ]

70});

71 71 

72# The response ID that can be used to continue the conversation later72console.log(text);

73 73 

74print(response.id)74// The response ID can be used to continue the conversation

75console.log(response.id);

75```76```

76 77 

77```python customLanguage="pythonOpenAISDK"78```python customLanguage="pythonOpenAISDK"


153console.log(response.id);154console.log(response.id);

154```155```

155 156 

156```javascript customLanguage="javascriptAISDK"

157import { xai } from '@ai-sdk/xai';

158import { generateText } from 'ai';

159 

160const { text, response } = await generateText({

161 model: xai.responses('grok-4.7'),

162 messages: [

163 {

164 role: 'user',

165 content: [

166 {

167 type: 'image',

168 image: new URL('https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png'),

169 },

170 {

171 type: 'text',

172 text: "What's in this image?",

173 },

174 ],

175 },

176 ]

177});

178 

179console.log(text);

180 

181// The response ID can be used to continue the conversation

182console.log(response.id);

183```

184 

185```bash157```bash

186curl https://api.x.ai/v1/responses \158curl https://api.x.ai/v1/responses \

187 -H "Content-Type: application/json" \159 -H "Content-Type: application/json" \


208 }'180 }'

209```181```

210 182 

183```python customLanguage="pythonXAI"

184import os

185from xai_sdk import Client

186from xai_sdk.chat import user, image

187 

188client = Client(

189 api_key=os.getenv("XAI_API_KEY"),

190 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

191 timeout=3600,

192)

193 

194image_url = "https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png"

195chat = client.chat.create(model="grok-4.7")

196chat.append(

197 user(

198 "What's in this image?",

199 image(image_url=image_url, detail="high"),

200 )

201)

202 

203response = chat.sample()

204print(response)

205 

206# The response ID that can be used to continue the conversation later

207 

208print(response.id)

209```

210 

211### Image input general limits211### Image input general limits

212 212 

213* Maximum image size: `20MiB`213* Maximum image size: `20MiB`

Details

12 12 

13Generate new images from text prompts with Grok Imagine models. Configure output count (up to 10 images per request), aspect ratio, resolution, and response format.13Generate new images from text prompts with Grok Imagine models. Configure output count (up to 10 images per request), aspect ratio, resolution, and response format.

14 14 

15```python customLanguage="pythonXAI"15```javascript customLanguage="javascriptAISDK"

16import xai_sdk16import { xai } from "@ai-sdk/xai";

17 17import { generateImage } from "ai";

18client = xai_sdk.Client()

19 

20response = client.image.sample(

21 prompt="A collage of London landmarks in a stenciled street‑art style",

22 model="grok-imagine-image-2.0",

23)

24 18 

25print(response.url)19const { image } = await generateImage({

26```20 model: xai.image("grok-imagine-image-2.0"),

21 prompt: "A collage of London landmarks in a stenciled street‑art style",

22});

27 23 

28```bash24console.log(image.base64);

29curl -X POST https://api.x.ai/v1/images/generations \

30 -H "Content-Type: application/json" \

31 -H "Authorization: Bearer $XAI_API_KEY" \

32 -d '{

33 "model": "grok-imagine-image-2.0",

34 "prompt": "A collage of London landmarks in a stenciled street‑art style"

35 }'

36```25```

37 26 

38```python customLanguage="pythonOpenAISDK"27```python customLanguage="pythonOpenAISDK"


51print(response.data[0].url)40print(response.data[0].url)

52```41```

53 42 

54```javascript customLanguage="javascriptAISDK"43```bash

55import { xai } from "@ai-sdk/xai";44curl -X POST https://api.x.ai/v1/images/generations \

56import { generateImage } from "ai";45 -H "Content-Type: application/json" \

57 46 -H "Authorization: Bearer $XAI_API_KEY" \

58const { image } = await generateImage({47 -d '{

59 model: xai.image("grok-imagine-image-2.0"),48 "model": "grok-imagine-image-2.0",

60 prompt: "A collage of London landmarks in a stenciled street‑art style",49 "prompt": "A collage of London landmarks in a stenciled street‑art style"

61});50 }'

62 

63console.log(image.base64);

64```51```

65 52 

66## Image Editing

67 

68Edit a source image with natural language. Provide a public image URL or base64-encoded data URI, then describe the change you want Grok Imagine to apply. Multi-image editing supports up to 5 source images in a single request for combining subjects, transferring styles, and composing scenes.

69 

70```python customLanguage="pythonXAI"53```python customLanguage="pythonXAI"

71import base64

72import xai_sdk54import xai_sdk

73 55 

74client = xai_sdk.Client()56client = xai_sdk.Client()

75 57 

76# Load image from file and encode as base64

77with open("photo.png", "rb") as f:

78 image_data = base64.b64encode(f.read()).decode("utf-8")

79 

80response = client.image.sample(58response = client.image.sample(

81 prompt="Render this as a pencil sketch with detailed shading",59 prompt="A collage of London landmarks in a stenciled street‑art style",

82 model="grok-imagine-image-2.0",60 model="grok-imagine-image-2.0",

83 image_url=f"data:image/png;base64,{image_data}",

84)61)

85 62 

86print(response.url)63print(response.url)

87```64```

88 65 

89```bash66## Image Editing

90# Using a public URL as the source image67 

91curl -X POST https://api.x.ai/v1/images/edits \68Edit a source image with natural language. Provide a public image URL or base64-encoded data URI, then describe the change you want Grok Imagine to apply. Multi-image editing supports up to 5 source images in a single request for combining subjects, transferring styles, and composing scenes.

92 -H "Content-Type: application/json" \

93 -H "Authorization: Bearer $XAI_API_KEY" \

94 -d '{

95 "model": "grok-imagine-image-2.0",

96 "prompt": "Render this as a pencil sketch with detailed shading",

97 "image": {

98 "url": "https://docs.x.ai/assets/api-examples/images/style-realistic.png",

99 "type": "image_url"

100 }

101 }'

102```

103 69 

104```javascript customLanguage="javascriptAISDK"70```javascript customLanguage="javascriptAISDK"

105import { xai } from "@ai-sdk/xai";71import { xai } from "@ai-sdk/xai";


121console.log(image.base64);87console.log(image.base64);

122```88```

123 89 

124## Video Generation90```bash

125 91# Using a public URL as the source image

126Animate a still image with a text prompt. The source image becomes the starting point for the generated video. Video requests are asynchronous: start a request, poll with the returned request ID, and use the completed video URL when ready. The xAI SDK and AI SDK handle polling for you.92curl -X POST https://api.x.ai/v1/images/edits \

93 -H "Content-Type: application/json" \

94 -H "Authorization: Bearer $XAI_API_KEY" \

95 -d '{

96 "model": "grok-imagine-image-2.0",

97 "prompt": "Render this as a pencil sketch with detailed shading",

98 "image": {

99 "url": "https://docs.x.ai/assets/api-examples/images/style-realistic.png",

100 "type": "image_url"

101 }

102 }'

103```

127 104 

128```python customLanguage="pythonXAI"105```python customLanguage="pythonXAI"

129import os106import base64

130import xai_sdk107import xai_sdk

131 108 

132client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))109client = xai_sdk.Client()

133 110 

134response = client.video.generate(111# Load image from file and encode as base64

135 prompt="Make the water crash down and slowly pan out the camera",112with open("photo.png", "rb") as f:

136 model="grok-imagine-video-1.5",113 image_data = base64.b64encode(f.read()).decode("utf-8")

137 image_url="https://docs.x.ai/assets/api-examples/video/waterfall-still.png",114 

138 duration=12,115response = client.image.sample(

116 prompt="Render this as a pencil sketch with detailed shading",

117 model="grok-imagine-image-2.0",

118 image_url=f"data:image/png;base64,{image_data}",

139)119)

140 120 

141print(response.url)121print(response.url)

142```122```

143 123 

124## Video Generation

125 

126Animate a still image with a text prompt. The source image becomes the starting point for the generated video. Video requests are asynchronous: start a request, poll with the returned request ID, and use the completed video URL when ready. The xAI SDK and AI SDK handle polling for you.

127 

144```javascript customLanguage="javascriptAISDK"128```javascript customLanguage="javascriptAISDK"

145import { xai } from "@ai-sdk/xai";129import { xai } from "@ai-sdk/xai";

146import { experimental_generateVideo as generateVideo } from "ai";130import { experimental_generateVideo as generateVideo } from "ai";


186done170done

187```171```

188 172 

173```python customLanguage="pythonXAI"

174import os

175import xai_sdk

176 

177client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

178 

179response = client.video.generate(

180 prompt="Make the water crash down and slowly pan out the camera",

181 model="grok-imagine-video-1.5",

182 image_url="https://docs.x.ai/assets/api-examples/video/waterfall-still.png",

183 duration=12,

184)

185 

186print(response.url)

187```

188 

189## More Capabilities189## More Capabilities

190 190 

191Beyond the top use cases above, the Imagine API supports several additional workflows:191Beyond the top use cases above, the Imagine API supports several additional workflows. The [Video Overview](/developers/model-capabilities/video/overview) compares the video models and shows each mode with real inputs and outputs.

192 192 

193* **[Multi-Image Editing](/developers/model-capabilities/images/multi-image-editing)** — Combine up to 5 source images in a single edit for compositing subjects, transferring styles, and building scenes from multiple references.193* **[Multi-Image Editing](/developers/model-capabilities/images/multi-image-editing)** — Combine up to 5 source images in a single edit for compositing subjects, transferring styles, and building scenes from multiple references.

194* **[Video Generation](/developers/model-capabilities/video/generation)** — Generate videos from text prompts with configurable duration (up to 15s), aspect ratio, and resolution.194* **[Video Generation](/developers/model-capabilities/video/generation)** — Generate videos from text prompts with configurable duration (up to 15s), aspect ratio, and resolution.

195* **[Video Editing](/developers/model-capabilities/video/editing)** — Modify an existing video with a text prompt while preserving the rest of the scene.195* **[Video Editing](/developers/model-capabilities/video/editing)** — Modify an existing video with a text prompt on `grok-imagine-video`.

196* **[Reference-to-Video](/developers/model-capabilities/video/reference-to-video)** — Guide a generated video with one or more reference images that influence the output without forcing the first frame.196* **[Reference-to-Video](/developers/model-capabilities/video/reference-to-video)** — Guide a video with up to 14 reference images and 3 voice references on `grok-imagine-video-1.5`, and pin first, last, and mid-video frames.

197* **[Video Extension](/developers/model-capabilities/video/extension)** — Continue an existing video from its last frame, combining the original and extension into one clip.197* **[Video Extension](/developers/model-capabilities/video/extension)** — Continue an existing video from its last frame, combining the original and extension into one clip.

198* **[Files API Integration](/developers/model-capabilities/imagine/files)** — Reference stored files as Imagine inputs by ID, persist generated assets to the Files API, and optionally create a permanent shareable public URL — all in a single request.198* **[Files API Integration](/developers/model-capabilities/imagine/files)** — Reference stored files as Imagine inputs by ID, persist generated assets to the Files API, and optionally create a permanent shareable public URL — all in a single request.

199 199 

Details

12 12 

13## Editing a stored image13## Editing a stored image

14 14 

15```bash

16curl -s -X POST https://api.x.ai/v1/images/edits \

17 -H "Authorization: Bearer $XAI_API_KEY" \

18 -H "Content-Type: application/json" \

19 -d '{

20 "model": "grok-imagine-image-quality",

21 "prompt": "Add a party hat to the dog",

22 "image": { "file_id": "file_7de029f4-eb66-42ee-87f8-b2a9d9e7466a" },

23 "response_format": "url"

24 }'

25```

26 

15```python customLanguage="pythonXAI"27```python customLanguage="pythonXAI"

16import os28import os

17import xai_sdk29import xai_sdk


27print(response.url)39print(response.url)

28```40```

29 41 

42## Editing with multiple stored images

43 

30```bash44```bash

45# Each images entry independently carries url or file_id — mix kinds within a single request.

31curl -s -X POST https://api.x.ai/v1/images/edits \46curl -s -X POST https://api.x.ai/v1/images/edits \

32 -H "Authorization: Bearer $XAI_API_KEY" \47 -H "Authorization: Bearer $XAI_API_KEY" \

33 -H "Content-Type: application/json" \48 -H "Content-Type: application/json" \

34 -d '{49 -d '{

35 "model": "grok-imagine-image-quality",50 "model": "grok-imagine-image-quality",

36 "prompt": "Add a party hat to the dog",51 "prompt": "Blend these two scenes into one cohesive composition",

37 "image": { "file_id": "file_7de029f4-eb66-42ee-87f8-b2a9d9e7466a" },52 "images": [

53 { "file_id": "file_7de029f4-eb66-42ee-87f8-b2a9d9e7466a" },

54 { "url": "https://example.com/scene-b.jpg" }

55 ],

38 "response_format": "url"56 "response_format": "url"

39 }'57 }'

40```58```

41 59 

42## Editing with multiple stored images

43 

44```python customLanguage="pythonXAI"60```python customLanguage="pythonXAI"

45response = client.image.sample(61response = client.image.sample(

46 prompt="Blend these two scenes into one cohesive composition",62 prompt="Blend these two scenes into one cohesive composition",


52)68)

53```69```

54 70 

71## Image-to-video from a stored first frame

72 

55```bash73```bash

56# Each images entry independently carries url or file_id — mix kinds within a single request.74curl -s -X POST https://api.x.ai/v1/videos/generations \

57curl -s -X POST https://api.x.ai/v1/images/edits \

58 -H "Authorization: Bearer $XAI_API_KEY" \75 -H "Authorization: Bearer $XAI_API_KEY" \

59 -H "Content-Type: application/json" \76 -H "Content-Type: application/json" \

60 -d '{77 -d '{

61 "model": "grok-imagine-image-quality",78 "model": "grok-imagine-video-1.5",

62 "prompt": "Blend these two scenes into one cohesive composition",79 "prompt": "Pan across the scene as the sky darkens",

63 "images": [80 "duration": 5,

64 { "file_id": "file_7de029f4-eb66-42ee-87f8-b2a9d9e7466a" },81 "image": { "file_id": "file_7de029f4-eb66-42ee-87f8-b2a9d9e7466a" }

65 { "url": "https://example.com/scene-b.jpg" }

66 ],

67 "response_format": "url"

68 }'82 }'

69```83```

70 84 

71## Image-to-video from a stored first frame

72 

73```python customLanguage="pythonXAI"85```python customLanguage="pythonXAI"

74response = client.video.generate(86response = client.video.generate(

75 prompt="Pan across the scene as the sky darkens",87 prompt="Pan across the scene as the sky darkens",


81print(response.url)93print(response.url)

82```94```

83 95 

84```bash

85curl -s -X POST https://api.x.ai/v1/videos/generations \

86 -H "Authorization: Bearer $XAI_API_KEY" \

87 -H "Content-Type: application/json" \

88 -d '{

89 "model": "grok-imagine-video-1.5",

90 "prompt": "Pan across the scene as the sky darkens",

91 "duration": 5,

92 "image": { "file_id": "file_7de029f4-eb66-42ee-87f8-b2a9d9e7466a" }

93 }'

94```

95 

96## Editing a stored video96## Editing a stored video

97 97 

98```python customLanguage="pythonXAI"

99response = client.video.generate(

100 prompt="Add rain and a moody atmosphere",

101 model="grok-imagine-video",

102 video_file_id="file_5be118c3-da55-31dd-76e7-a1b8c8d6355b",

103)

104```

105 

106```bash98```bash

107curl -s -X POST https://api.x.ai/v1/videos/edits \99curl -s -X POST https://api.x.ai/v1/videos/edits \

108 -H "Authorization: Bearer $XAI_API_KEY" \100 -H "Authorization: Bearer $XAI_API_KEY" \


114 }'106 }'

115```107```

116 108 

117## Reference-to-video with multiple stored images

118 

119```python customLanguage="pythonXAI"109```python customLanguage="pythonXAI"

120response = client.video.generate(110response = client.video.generate(

121 prompt="A woman in this dress walks down a city street at night",111 prompt="Add rain and a moody atmosphere",

122 model="grok-imagine-video-1.5",112 model="grok-imagine-video",

123 duration=5,113 video_file_id="file_5be118c3-da55-31dd-76e7-a1b8c8d6355b",

124 reference_image_file_ids=[

125 "file_5be118c3-da55-31dd-76e7-a1b8c8d6355b", # subject

126 "file_2cd998e7-bf12-44aa-92c8-e3d1f1c1234f", # outfit

127 ],

128)114)

129```115```

130 116 

117## Reference-to-video with multiple stored images

118 

131```bash119```bash

132# Each reference_images entry independently carries url or file_id — mix kinds within a single request.120# Each reference_images entry independently carries url or file_id — mix kinds within a single request.

133curl -s -X POST https://api.x.ai/v1/videos/generations \121curl -s -X POST https://api.x.ai/v1/videos/generations \


144 }'132 }'

145```133```

146 134 

135```python customLanguage="pythonXAI"

136response = client.video.generate(

137 prompt="A woman in this dress walks down a city street at night",

138 model="grok-imagine-video-1.5",

139 duration=5,

140 reference_image_file_ids=[

141 "file_5be118c3-da55-31dd-76e7-a1b8c8d6355b", # subject

142 "file_2cd998e7-bf12-44aa-92c8-e3d1f1c1234f", # outfit

143 ],

144)

145```

146 

147## Related147## Related

148 148 

149* [Files API Integration](/developers/model-capabilities/imagine/files) — Overview + capstone example showing inputs and outputs together.149* [Files API Integration](/developers/model-capabilities/imagine/files) — Overview + capstone example showing inputs and outputs together.

Details

10 10 

11Generate an image, persist it to Files, and get a shareable public URL — all in one call:11Generate an image, persist it to Files, and get a shareable public URL — all in one call:

12 12 

13```bash

14curl -s -X POST https://api.x.ai/v1/images/generations \

15 -H "Authorization: Bearer $XAI_API_KEY" \

16 -H "Content-Type: application/json" \

17 -d '{

18 "model": "grok-imagine-image-quality",

19 "prompt": "A serene Japanese garden in winter",

20 "response_format": "url",

21 "storage_options": {

22 "filename": "garden.jpg",

23 "public_url": true

24 }

25 }'

26```

27 

13```python customLanguage="pythonXAI"28```python customLanguage="pythonXAI"

14import os29import os

15import xai_sdk30import xai_sdk


32print(f"Public URL: {response.public_url}")47print(f"Public URL: {response.public_url}")

33```48```

34 49 

35```bash

36curl -s -X POST https://api.x.ai/v1/images/generations \

37 -H "Authorization: Bearer $XAI_API_KEY" \

38 -H "Content-Type: application/json" \

39 -d '{

40 "model": "grok-imagine-image-quality",

41 "prompt": "A serene Japanese garden in winter",

42 "response_format": "url",

43 "storage_options": {

44 "filename": "garden.jpg",

45 "public_url": true

46 }

47 }'

48```

49 

50The response body looks like this:50The response body looks like this:

51 51 

52```json52```json


81 81 

82`filename` is required. Passing `storage_options={"filename": "..."}` with no other fields persists the asset privately: no expiry on the stored file and no public URL. You can always call [`create_public_url`](/developers/files/public-urls) on the stored `file_id` later if you change your mind.82`filename` is required. Passing `storage_options={"filename": "..."}` with no other fields persists the asset privately: no expiry on the stored file and no public URL. You can always call [`create_public_url`](/developers/files/public-urls) on the stored `file_id` later if you change your mind.

83 83 

84```bash

85curl -s -X POST https://api.x.ai/v1/images/generations \

86 -H "Authorization: Bearer $XAI_API_KEY" \

87 -H "Content-Type: application/json" \

88 -d '{

89 "model": "grok-imagine-image-quality",

90 "prompt": "A red circle on a white background",

91 "response_format": "url",

92 "storage_options": {"filename": "circle.jpg"}

93 }'

94```

95 

84```python customLanguage="pythonXAI"96```python customLanguage="pythonXAI"

85response = client.image.sample(97response = client.image.sample(

86 prompt="A red circle on a white background",98 prompt="A red circle on a white background",


93print(response.public_url) # None — public URL was not requested105print(response.public_url) # None — public URL was not requested

94```106```

95 107 

108## Expiry Behaviour

109 

110`storage_options` exposes two independent expiry knobs: `storage_options.expires_after` controls when the **stored file** auto-deletes, and `storage_options.public_url.expires_after` controls when the **public URL** auto-revokes. Omit `public_url.expires_after` and the URL inherits the file's expiry (or never expires if the file has none).

111 

112A public URL can never outlive its file, and both values must be between **1 hour and 30 days**. See [Public URLs → Expiry Behaviour](/developers/files/public-urls#expiry-behaviour) for the full rules; the examples below show how the two knobs combine on an Imagine request.

113 

96```bash114```bash

115# Permanent file, 24h public URL

97curl -s -X POST https://api.x.ai/v1/images/generations \116curl -s -X POST https://api.x.ai/v1/images/generations \

98 -H "Authorization: Bearer $XAI_API_KEY" \117 -H "Authorization: Bearer $XAI_API_KEY" \

99 -H "Content-Type: application/json" \118 -H "Content-Type: application/json" \

100 -d '{119 -d '{

101 "model": "grok-imagine-image-quality",120 "model": "grok-imagine-image-quality",

102 "prompt": "A red circle on a white background",121 "prompt": "A futuristic city skyline at night",

103 "response_format": "url",122 "response_format": "url",

104 "storage_options": {"filename": "circle.jpg"}123 "storage_options": {

124 "filename": "skyline.jpg",

125 "public_url": {"expires_after": 86400}

126 }

105 }'127 }'

106```

107 128 

108## Expiry Behaviour129# 2h file, public URL inherits the same expiry

109 130curl -s -X POST https://api.x.ai/v1/images/generations \

110`storage_options` exposes two independent expiry knobs: `storage_options.expires_after` controls when the **stored file** auto-deletes, and `storage_options.public_url.expires_after` controls when the **public URL** auto-revokes. Omit `public_url.expires_after` and the URL inherits the file's expiry (or never expires if the file has none).131 -H "Authorization: Bearer $XAI_API_KEY" \

111 132 -H "Content-Type: application/json" \

112A public URL can never outlive its file, and both values must be between **1 hour and 30 days**. See [Public URLs → Expiry Behaviour](/developers/files/public-urls#expiry-behaviour) for the full rules; the examples below show how the two knobs combine on an Imagine request.133 -d '{

134 "model": "grok-imagine-image-quality",

135 "prompt": "A futuristic city skyline at night",

136 "response_format": "url",

137 "storage_options": {

138 "filename": "skyline.jpg",

139 "expires_after": 7200,

140 "public_url": true

141 }

142 }'

143```

113 144 

114```python customLanguage="pythonXAI"145```python customLanguage="pythonXAI"

115import os146import os


154print(response.file_output.public_url_expires_at) # ~1h from now (URL dies before file)185print(response.file_output.public_url_expires_at) # ~1h from now (URL dies before file)

155```186```

156 187 

157```bash

158# Permanent file, 24h public URL

159curl -s -X POST https://api.x.ai/v1/images/generations \

160 -H "Authorization: Bearer $XAI_API_KEY" \

161 -H "Content-Type: application/json" \

162 -d '{

163 "model": "grok-imagine-image-quality",

164 "prompt": "A futuristic city skyline at night",

165 "response_format": "url",

166 "storage_options": {

167 "filename": "skyline.jpg",

168 "public_url": {"expires_after": 86400}

169 }

170 }'

171 

172# 2h file, public URL inherits the same expiry

173curl -s -X POST https://api.x.ai/v1/images/generations \

174 -H "Authorization: Bearer $XAI_API_KEY" \

175 -H "Content-Type: application/json" \

176 -d '{

177 "model": "grok-imagine-image-quality",

178 "prompt": "A futuristic city skyline at night",

179 "response_format": "url",

180 "storage_options": {

181 "filename": "skyline.jpg",

182 "expires_after": 7200,

183 "public_url": true

184 }

185 }'

186```

187 

188## The `file_output` Response188## The `file_output` Response

189 189 

190Every Imagine response with `storage_options` set includes a `file_output` block on each generated asset:190Every Imagine response with `storage_options` set includes a `file_output` block on each generated asset:


204 204 

205When you request multiple images in a single call, each image gets its own `file_id` and its own `public_url` with a unique token. The files are completely independent — revoking or deleting one does not affect the others.205When you request multiple images in a single call, each image gets its own `file_id` and its own `public_url` with a unique token. The files are completely independent — revoking or deleting one does not affect the others.

206 206 

207```python customLanguage="pythonXAI"

208import os

209import xai_sdk

210 

211client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

212 

213responses = client.image.sample_batch(

214 prompt="A cat wearing a hat, four different art styles",

215 model="grok-imagine-image-quality",

216 n=4,

217 storage_options={"filename": "cat-styles.jpg", "public_url": True},

218)

219 

220for r in responses:

221 print(r.file_output.file_id, r.public_url)

222```

223 

224```bash207```bash

225curl -s -X POST https://api.x.ai/v1/images/generations \208curl -s -X POST https://api.x.ai/v1/images/generations \

226 -H "Authorization: Bearer $XAI_API_KEY" \209 -H "Authorization: Bearer $XAI_API_KEY" \


234 }'217 }'

235```218```

236 219 

237## Storing Image Edit Outputs

238 

239`storage_options` works on `/v1/images/edits` the same way as `/v1/images/generations`. The edited result is stored as a new file.

240 

241```python customLanguage="pythonXAI"220```python customLanguage="pythonXAI"

242import os221import os

243import xai_sdk222import xai_sdk

244 223 

245client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))224client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

246 225 

247response = client.image.sample(226responses = client.image.sample_batch(

248 prompt="Add a party hat to the dog",227 prompt="A cat wearing a hat, four different art styles",

249 model="grok-imagine-image-quality",228 model="grok-imagine-image-quality",

250 image_url="https://docs.x.ai/assets/api-examples/images/style-realistic.png",229 n=4,

251 storage_options={"filename": "party-dog.png", "public_url": True},230 storage_options={"filename": "cat-styles.jpg", "public_url": True},

252)231)

253 232 

254print(response.file_output.file_id) # new file, not the input233for r in responses:

255print(response.public_url)234 print(r.file_output.file_id, r.public_url)

256```235```

257 236 

237## Storing Image Edit Outputs

238 

239`storage_options` works on `/v1/images/edits` the same way as `/v1/images/generations`. The edited result is stored as a new file.

240 

258```bash241```bash

259curl -s -X POST https://api.x.ai/v1/images/edits \242curl -s -X POST https://api.x.ai/v1/images/edits \

260 -H "Authorization: Bearer $XAI_API_KEY" \243 -H "Authorization: Bearer $XAI_API_KEY" \


270 }'253 }'

271```254```

272 255 

273## Storing Video Outputs

274 

275The video endpoints (`/v1/videos/generations`, `/v1/videos/edits`, `/v1/videos/extensions`) use the same `storage_options` shape. Since video generation is asynchronous, `file_output.public_url` is populated on the **completed** response after the video finishes generating.

276 

277```python customLanguage="pythonXAI"256```python customLanguage="pythonXAI"

278import os257import os

279import xai_sdk258import xai_sdk

280 259 

281client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))260client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

282 261 

283# SDK handles polling automatically and returns the completed video.262response = client.image.sample(

284response = client.video.generate(263 prompt="Add a party hat to the dog",

285 prompt="A ball bouncing slowly on a flat surface",264 model="grok-imagine-image-quality",

286 model="grok-imagine-video-1.5",265 image_url="https://docs.x.ai/assets/api-examples/images/style-realistic.png",

287 duration=5,266 storage_options={"filename": "party-dog.png", "public_url": True},

288 storage_options={"filename": "bouncing-ball.mp4", "public_url": True},

289)267)

290 268 

291print(response.url) # ephemeral vidgen URL269print(response.file_output.file_id) # new file, not the input

292print(response.file_output.file_id) # file_...270print(response.public_url)

293print(response.public_url) # https://files-cdn.x.ai/<token>/file_....mp4

294```271```

295 272 

273## Storing Video Outputs

274 

275The video endpoints (`/v1/videos/generations`, `/v1/videos/edits`, `/v1/videos/extensions`) use the same `storage_options` shape. Since video generation is asynchronous, `file_output.public_url` is populated on the **completed** response after the video finishes generating.

276 

296```bash277```bash

297# Start the generation278# Start the generation

298REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \279REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \


313 if [ "$STATUS" = "done" ]; then294 if [ "$STATUS" = "done" ]; then

314 echo "$RESULT" | jq '.video.file_output'295 echo "$RESULT" | jq '.video.file_output'

315 break296 break

297 elif [ "$STATUS" = "failed" ]; then

298 echo "$RESULT" | jq .

299 break

316 fi300 fi

317 sleep 5301 sleep 5

318done302done

319```303```

320 304 

305```python customLanguage="pythonXAI"

306import os

307import xai_sdk

308 

309client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

310 

311# SDK handles polling automatically and returns the completed video.

312response = client.video.generate(

313 prompt="A ball bouncing slowly on a flat surface",

314 model="grok-imagine-video-1.5",

315 duration=5,

316 storage_options={"filename": "bouncing-ball.mp4", "public_url": True},

317)

318 

319print(response.url) # ephemeral vidgen URL

320print(response.file_output.file_id) # file_...

321print(response.public_url) # https://files-cdn.x.ai/<token>/file_....mp4

322```

323 

321Image-to-video, video editing, and video extension all accept `storage_options` the same way.324Image-to-video, video editing, and video extension all accept `storage_options` the same way.

322 325 

323## Public URL Errors326## Public URL Errors


367 370 

368Files created through `storage_options` are full first-class Files API files. Use the [Files API](/developers/files/managing-files) to list, retrieve, update, and delete them, and use the public URL endpoints to revoke or re-create the URL after the fact:371Files created through `storage_options` are full first-class Files API files. Use the [Files API](/developers/files/managing-files) to list, retrieve, update, and delete them, and use the public URL endpoints to revoke or re-create the URL after the fact:

369 372 

373```bash

374FILE_ID="<file_output.file_id from the generation response>"

375 

376# Stop sharing publicly (file stays in your storage)

377curl -s -X POST "https://api.x.ai/v1/files/$FILE_ID/public-url/revoke" \

378 -H "Authorization: Bearer $XAI_API_KEY"

379 

380# Re-create with a 7-day expiry

381curl -s -X POST "https://api.x.ai/v1/files/$FILE_ID/public-url" \

382 -H "Authorization: Bearer $XAI_API_KEY" \

383 -H "Content-Type: application/json" \

384 -d '{"expires_after": 604800}'

385 

386# Delete the file (also revokes the public URL)

387curl -s -X DELETE "https://api.x.ai/v1/files/$FILE_ID" \

388 -H "Authorization: Bearer $XAI_API_KEY"

389```

390 

370```python customLanguage="pythonXAI"391```python customLanguage="pythonXAI"

371import os392import os

372import xai_sdk393import xai_sdk


393client.files.delete(file_id)414client.files.delete(file_id)

394```415```

395 416 

396```bash

397FILE_ID="<file_output.file_id from the generation response>"

398 

399# Stop sharing publicly (file stays in your storage)

400curl -s -X POST "https://api.x.ai/v1/files/$FILE_ID/public-url/revoke" \

401 -H "Authorization: Bearer $XAI_API_KEY"

402 

403# Re-create with a 7-day expiry

404curl -s -X POST "https://api.x.ai/v1/files/$FILE_ID/public-url" \

405 -H "Authorization: Bearer $XAI_API_KEY" \

406 -H "Content-Type: application/json" \

407 -d '{"expires_after": 604800}'

408 

409# Delete the file (also revokes the public URL)

410curl -s -X DELETE "https://api.x.ai/v1/files/$FILE_ID" \

411 -H "Authorization: Bearer $XAI_API_KEY"

412```

413 

414## Limitations417## Limitations

415 418 

416* **Up to 1,000 active public URLs per team.** Hitting the cap sets `public_url_error` on the response; revoke unused URLs to free up slots.419* **Up to 1,000 active public URLs per team.** Hitting the cap sets `public_url_error` on the response; revoke unused URLs to free up slots.

Details

22 22 

23The user sends a request to the xAI API endpoint. The API processes this and returns a complete response.23The user sends a request to the xAI API endpoint. The API processes this and returns a complete response.

24 24 

25```python customLanguage="pythonXAI"25```javascript customLanguage="javascriptAISDK"

26import os26import { xai } from '@ai-sdk/xai';

27 27import { generateText } from 'ai';

28from xai_sdk import Client

29from xai_sdk.chat import user, system

30 

31client = Client(

32 api_key=os.getenv("XAI_API_KEY"),

33 timeout=3600, # Override default timeout with longer timeout for reasoning models

34)

35 28 

36chat = client.chat.create(model="grok-4.7")29const result = await generateText({

37chat.append(system("You are a PhD-level mathematician."))30 model: xai('grok-4.7'),

38chat.append(user("What is 2 + 2?"))31 system:

32 "You are Grok, a helpful and useful AI built by xAI.",

33 prompt: 'Explain how neural networks learn in two sentences.',

34});

39 35 

40response = chat.sample()36console.log(result.text);

41print(response.content)

42```37```

43 38 

44```python customLanguage="pythonOpenAISDK"39```python customLanguage="pythonOpenAISDK"


89console.log(completion.choices[0].message);84console.log(completion.choices[0].message);

90```85```

91 86 

92```javascript customLanguage="javascriptAISDK"

93import { xai } from '@ai-sdk/xai';

94import { generateText } from 'ai';

95 

96const result = await generateText({

97 model: xai('grok-4.7'),

98 system:

99 "You are Grok, a helpful and useful AI built by xAI.",

100 prompt: 'Explain how neural networks learn in two sentences.',

101});

102 

103console.log(result.text);

104```

105 

106```bash87```bash

107curl https://api.x.ai/v1/chat/completions \88curl https://api.x.ai/v1/chat/completions \

108-H "Content-Type: application/json" \89-H "Content-Type: application/json" \


124}'105}'

125```106```

126 107 

108```python customLanguage="pythonXAI"

109import os

110 

111from xai_sdk import Client

112from xai_sdk.chat import user, system

113 

114client = Client(

115 api_key=os.getenv("XAI_API_KEY"),

116 timeout=3600, # Override default timeout with longer timeout for reasoning models

117)

118 

119chat = client.chat.create(model="grok-4.7")

120chat.append(system("You are a PhD-level mathematician."))

121chat.append(user("What is 2 + 2?"))

122 

123response = chat.sample()

124print(response.content)

125```

126 

127Response:127Response:

128 128 

129```python customLanguage="pythonXAI"129```javascript customLanguage="javascriptAISDK"

130'2 + 2 equals 4.'130// result object structure

131{

132 text: "Neural networks learn by adjusting connection weights...",

133 finishReason: "stop",

134 usage: {

135 inputTokens: 716,

136 outputTokens: 126,

137 totalTokens: 1009,

138 reasoningTokens: 167

139 },

140 totalUsage: { /* same as usage */ }

141}

131```142```

132 143 

133```python customLanguage="pythonOpenAISDK"144```python customLanguage="pythonOpenAISDK"


149}160}

150```161```

151 162 

152```javascript customLanguage="javascriptAISDK"

153// result object structure

154{

155 text: "Neural networks learn by adjusting connection weights...",

156 finishReason: "stop",

157 usage: {

158 inputTokens: 716,

159 outputTokens: 126,

160 totalTokens: 1009,

161 reasoningTokens: 167

162 },

163 totalUsage: { /* same as usage */ }

164}

165```

166 

167```bash163```bash

168{164{

169 "id": "0daf962f-a275-4a3c-839a-047854645532",165 "id": "0daf962f-a275-4a3c-839a-047854645532",


196}192}

197```193```

198 194 

195```python customLanguage="pythonXAI"

196'2 + 2 equals 4.'

197```

198 

199## Conversations199## Conversations

200 200 

201The xAI API is stateless and does not process a new request with the context of your previous request history.201The xAI API is stateless and does not process a new request with the context of your previous request history.


272 272 

273### Image understanding example273### Image understanding example

274 274 

275```pythonXAI275```javascriptAISDK

276import os276import { xai } from '@ai-sdk/xai';

277 277import { generateText } from 'ai';

278from xai_sdk import Client

279from xai_sdk.chat import user, image

280 

281client = Client(api_key=os.getenv('XAI_API_KEY'))

282 

283image_url = "https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png"

284 278 

285chat = client.chat.create(model="grok-4")279const result = await generateText({

286chat.append(280model: xai('grok-4'),

287 user(281messages: [

288 "What's in this image?",282 {

289 image(image_url=image_url, detail="high"),283 role: 'user',

290 )284 content: [

291)285 {

286 type: 'image',

287 image: new URL(

288 'https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png',

289 ),

290 },

291 {

292 type: 'text',

293 text: "What's in this image?",

294 },

295 ],

296 },

297 ],

298});

292 299 

293response = chat.sample()300console.log(result.text);

294print(response.content)

295```301```

296 302 

297```pythonOpenAISDK303```pythonOpenAISDK


370console.log(completion.choices[0].message.content);376console.log(completion.choices[0].message.content);

371```377```

372 378 

373```javascriptAISDK379```pythonXAI

374import { xai } from '@ai-sdk/xai';380import os

375import { generateText } from 'ai';

376 381 

377const result = await generateText({382from xai_sdk import Client

378model: xai('grok-4'),383from xai_sdk.chat import user, image

379messages: [

380 {

381 role: 'user',

382 content: [

383 {

384 type: 'image',

385 image: new URL(

386 'https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png',

387 ),

388 },

389 {

390 type: 'text',

391 text: "What's in this image?",

392 },

393 ],

394 },

395 ],

396});

397 384 

398console.log(result.text);385client = Client(api_key=os.getenv('XAI_API_KEY'))

386 

387image_url = "https://science.nasa.gov/wp-content/uploads/2023/09/web-first-images-release.png"

388 

389chat = client.chat.create(model="grok-4")

390chat.append(

391 user(

392 "What's in this image?",

393 image(image_url=image_url, detail="high"),

394 )

395)

396 

397response = chat.sample()

398print(response.content)

399```399```

400 400 

401### Image input general limits401### Image input general limits

Details

23 23 

24Start by creating a response:24Start by creating a response:

25 25 

26```python customLanguage="pythonXAI"26```javascript customLanguage="javascriptAISDK"

27import os27import { xai } from '@ai-sdk/xai';

28from xai_sdk import Client28import { generateText } from 'ai';

29from xai_sdk.chat import user, system

30 

31client = Client(

32 api_key=os.getenv("XAI_API_KEY"),

33 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

34 timeout=3600,

35)

36 

37chat = client.chat.create(model="grok-4.7")

38chat.append(system("You are Grok, an AI agent built to answer helpful questions."))

39chat.append(user("How big is the universe?"))

40response = chat.sample()

41 29 

42print(response)30const { text, response } = await generateText({

31 model: xai.responses('grok-4.7'),

32 system: "You are Grok, an AI agent built to answer helpful questions.",

33 prompt: "How big is the universe?",

34});

43 35 

44# The response ID that can be used to continue the conversation later36console.log(text);

45 37 

46print(response.id)38// The response ID can be used to continue the conversation

39console.log(response.id);

47```40```

48 41 

49```python customLanguage="pythonOpenAISDK"42```python customLanguage="pythonOpenAISDK"


101console.log(response.id);94console.log(response.id);

102```95```

103 96 

104```javascript customLanguage="javascriptAISDK"

105import { xai } from '@ai-sdk/xai';

106import { generateText } from 'ai';

107 

108const { text, response } = await generateText({

109 model: xai.responses('grok-4.7'),

110 system: "You are Grok, an AI agent built to answer helpful questions.",

111 prompt: "How big is the universe?",

112});

113 

114console.log(text);

115 

116// The response ID can be used to continue the conversation

117console.log(response.id);

118```

119 

120```bash97```bash

121curl https://api.x.ai/v1/responses \98curl https://api.x.ai/v1/responses \

122 -H "Content-Type: application/json" \99 -H "Content-Type: application/json" \


137}'114}'

138```115```

139 116 

140### Disable storing previous request/response on server

141 

142If you do not want to store your previous request/response on the server, you can set `store: false` on the request.

143 

144```python customLanguage="pythonXAI"117```python customLanguage="pythonXAI"

145import os118import os

146from xai_sdk import Client119from xai_sdk import Client


152 timeout=3600,125 timeout=3600,

153)126)

154 127 

155chat = client.chat.create(model="grok-4.7", store_messages=False)128chat = client.chat.create(model="grok-4.7")

156chat.append(system("You are Grok, an AI agent built to answer helpful questions."))129chat.append(system("You are Grok, an AI agent built to answer helpful questions."))

157chat.append(user("How big is the universe?"))130chat.append(user("How big is the universe?"))

158response = chat.sample()131response = chat.sample()

159 132 

160print(response)133print(response)

134 

135# The response ID that can be used to continue the conversation later

136 

137print(response.id)

161```138```

162 139 

140### Disable storing previous request/response on server

141 

142If you do not want to store your previous request/response on the server, you can set `store: false` on the request.

143 

163```python customLanguage="pythonOpenAISDK"144```python customLanguage="pythonOpenAISDK"

164import os145import os

165import httpx146import httpx


231}'212}'

232```213```

233 214 

215```python customLanguage="pythonXAI"

216import os

217from xai_sdk import Client

218from xai_sdk.chat import user, system

219 

220client = Client(

221 api_key=os.getenv("XAI_API_KEY"),

222 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

223 timeout=3600,

224)

225 

226chat = client.chat.create(model="grok-4.7", store_messages=False)

227chat.append(system("You are Grok, an AI agent built to answer helpful questions."))

228chat.append(user("How big is the universe?"))

229response = chat.sample()

230 

231print(response)

232```

233 

234### Returning encrypted thinking content234### Returning encrypted thinking content

235 235 

236If you want to return the encrypted thinking traces, you need to specify `use_encrypted_content=True` in xAI SDK or gRPC request message, or `include: ["reasoning.encrypted_content"]` in the request body.236If you want to return the encrypted thinking traces, you need to specify `use_encrypted_content=True` in xAI SDK or gRPC request message, or `include: ["reasoning.encrypted_content"]` in the request body.


245 245 

246Modify the steps to create a chat client (xAI SDK) or change the request body as following:246Modify the steps to create a chat client (xAI SDK) or change the request body as following:

247 247 

248```python customLanguage="pythonXAI"248```javascript customLanguage="javascriptAISDK"

249chat = client.chat.create(model="grok-4.7",249import { xai } from '@ai-sdk/xai';

250 use_encrypted_content=True)250import { generateText } from 'ai';

251 

252// Encrypted reasoning content is included automatically by the AI SDK

253// as long as `store: false` is not set. No extra configuration is needed.

254const { text, reasoning } = await generateText({

255 model: xai.responses('grok-4.7'),

256 system: "You are Grok, an AI agent built to answer helpful questions.",

257 prompt: "How big is the universe?",

258});

259 

260console.log(text);

261console.log(reasoning); // Contains encrypted reasoning content

251```262```

252 263 

253```python customLanguage="pythonOpenAISDK"264```python customLanguage="pythonOpenAISDK"


273 284 

274```285```

275 286 

276```javascript customLanguage="javascriptAISDK"

277import { xai } from '@ai-sdk/xai';

278import { generateText } from 'ai';

279 

280// Encrypted reasoning content is included automatically by the AI SDK

281// as long as `store: false` is not set. No extra configuration is needed.

282const { text, reasoning } = await generateText({

283 model: xai.responses('grok-4.7'),

284 system: "You are Grok, an AI agent built to answer helpful questions.",

285 prompt: "How big is the universe?",

286});

287 

288console.log(text);

289console.log(reasoning); // Contains encrypted reasoning content

290```

291 

292```bash287```bash

293curl https://api.x.ai/v1/responses \288curl https://api.x.ai/v1/responses \

294 -H "Content-Type: application/json" \289 -H "Content-Type: application/json" \


310}'305}'

311```306```

312 307 

308```python customLanguage="pythonXAI"

309chat = client.chat.create(model="grok-4.7",

310 use_encrypted_content=True)

311```

312 

313See [Adding encrypted thinking content](#adding-encrypted-thinking-content) on how to use the returned encrypted thinking content when making a new request.313See [Adding encrypted thinking content](#adding-encrypted-thinking-content) on how to use the returned encrypted thinking content when making a new request.

314 314 

315## Chaining the conversation315## Chaining the conversation


318 318 

319With Responses API, we can send the `id` of the previous response, and the new messages to append to it.319With Responses API, we can send the `id` of the previous response, and the new messages to append to it.

320 320 

321```python customLanguage="pythonXAI"321```javascript customLanguage="javascriptAISDK"

322import os322import { xai } from '@ai-sdk/xai';

323from xai_sdk import Client323import { generateText } from 'ai';

324from xai_sdk.chat import user, system

325 

326client = Client(

327 api_key=os.getenv("XAI_API_KEY"),

328 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

329 timeout=3600,

330)

331 

332chat = client.chat.create(model="grok-4.7", store_messages=True)

333chat.append(system("You are Grok, an AI agent built to answer helpful questions."))

334chat.append(user("How big is the universe?"))

335response = chat.sample()

336 

337print(response)

338 

339# The response ID that can be used to continue the conversation later

340 

341print(response.id)

342 324 

343# New steps325// First request

326const result = await generateText({

327 model: xai.responses('grok-4.7'),

328 system: "You are Grok, an AI agent built to answer helpful questions.",

329 prompt: "How big is the universe?",

330});

344 331 

345chat = client.chat.create(332console.log(result.text);

346 model="grok-4.7",

347 previous_response_id=response.id,

348 store_messages=True,

349)

350chat.append(user("How do stars form?"))

351second_response = chat.sample()

352 333 

353print(second_response)334// Get the response ID from the response object

335const responseId = result.response.id;

354 336 

355# The response ID that can be used to continue the conversation later337// Continue the conversation using previousResponseId

338const { text: secondResponse } = await generateText({

339 model: xai.responses('grok-4.7'),

340 prompt: "How do stars form?",

341 providerOptions: {

342 xai: {

343 previousResponseId: responseId,

344 },

345 },

346});

356 347 

357print(second_response.id)348console.log(secondResponse);

358```349```

359 350 

360```python customLanguage="pythonOpenAISDK"351```python customLanguage="pythonOpenAISDK"


443console.log(secondResponse.id);434console.log(secondResponse.id);

444```435```

445 436 

446```javascript customLanguage="javascriptAISDK"

447import { xai } from '@ai-sdk/xai';

448import { generateText } from 'ai';

449 

450// First request

451const result = await generateText({

452 model: xai.responses('grok-4.7'),

453 system: "You are Grok, an AI agent built to answer helpful questions.",

454 prompt: "How big is the universe?",

455});

456 

457console.log(result.text);

458 

459// Get the response ID from the response object

460const responseId = result.response.id;

461 

462// Continue the conversation using previousResponseId

463const { text: secondResponse } = await generateText({

464 model: xai.responses('grok-4.7'),

465 prompt: "How do stars form?",

466 providerOptions: {

467 xai: {

468 previousResponseId: responseId,

469 },

470 },

471});

472 

473console.log(secondResponse);

474```

475 

476```bash437```bash

477curl https://api.x.ai/v1/responses \438curl https://api.x.ai/v1/responses \

478 -H "Content-Type: application/json" \439 -H "Content-Type: application/json" \


490}'451}'

491```452```

492 453 

493### Adding encrypted thinking content

494 

495After returning the encrypted thinking content, you can also add it to a new response's input.

496 

497> [!NOTE]

498>

499> Make sure to use a reasoning model when working with encrypted thinking content.

500 

501```python customLanguage="pythonXAI"454```python customLanguage="pythonXAI"

502import os455import os

503from xai_sdk import Client456from xai_sdk import Client


509 timeout=3600,462 timeout=3600,

510)463)

511 464 

512chat = client.chat.create(model="grok-4.7", store_messages=True, use_encrypted_content=True)465chat = client.chat.create(model="grok-4.7", store_messages=True)

513chat.append(system("You are Grok, an AI agent built to answer helpful questions."))466chat.append(system("You are Grok, an AI agent built to answer helpful questions."))

514chat.append(user("How big is the universe?"))467chat.append(user("How big is the universe?"))

515response = chat.sample()468response = chat.sample()


522 475 

523# New steps476# New steps

524 477 

525chat.append(response) ## Append the response and the SDK will automatically add the outputs from response to message history478chat = client.chat.create(

526 479 model="grok-4.7",

480 previous_response_id=response.id,

481 store_messages=True,

482)

527chat.append(user("How do stars form?"))483chat.append(user("How do stars form?"))

528second_response = chat.sample()484second_response = chat.sample()

529 485 


534print(second_response.id)490print(second_response.id)

535```491```

536 492 

493### Adding encrypted thinking content

494 

495After returning the encrypted thinking content, you can also add it to a new response's input.

496 

497> [!NOTE]

498>

499> Make sure to use a reasoning model when working with encrypted thinking content.

500 

501```javascript customLanguage="javascriptAISDK"

502import { xai } from '@ai-sdk/xai';

503import { generateText } from 'ai';

504 

505// First request. Encrypted reasoning content is included automatically

506// by the AI SDK as long as `store: false` is not set.

507const result = await generateText({

508 model: xai.responses('grok-4.7'),

509 system: "You are Grok, an AI agent built to answer helpful questions.",

510 prompt: "How big is the universe?",

511});

512 

513console.log(result.text);

514 

515// Continue the conversation using previousResponseId

516// The encrypted content is automatically included when using previousResponseId

517const { text: secondResponse } = await generateText({

518 model: xai.responses('grok-4.7'),

519 prompt: "How do stars form?",

520 providerOptions: {

521 xai: {

522 previousResponseId: result.response.id,

523 },

524 },

525});

526 

527console.log(secondResponse);

528```

529 

537```python customLanguage="pythonOpenAISDK"530```python customLanguage="pythonOpenAISDK"

538# Previous steps531# Previous steps

539import os532import os


622console.log(secondResponse.id);615console.log(secondResponse.id);

623```616```

624 617 

625```javascript customLanguage="javascriptAISDK"

626import { xai } from '@ai-sdk/xai';

627import { generateText } from 'ai';

628 

629// First request. Encrypted reasoning content is included automatically

630// by the AI SDK as long as `store: false` is not set.

631const result = await generateText({

632 model: xai.responses('grok-4.7'),

633 system: "You are Grok, an AI agent built to answer helpful questions.",

634 prompt: "How big is the universe?",

635});

636 

637console.log(result.text);

638 

639// Continue the conversation using previousResponseId

640// The encrypted content is automatically included when using previousResponseId

641const { text: secondResponse } = await generateText({

642 model: xai.responses('grok-4.7'),

643 prompt: "How do stars form?",

644 providerOptions: {

645 xai: {

646 previousResponseId: result.response.id,

647 },

648 },

649});

650 

651console.log(secondResponse);

652```

653 

654```bash618```bash

655curl https://api.x.ai/v1/responses \619curl https://api.x.ai/v1/responses \

656 -H "Content-Type: application/json" \620 -H "Content-Type: application/json" \


699}'663}'

700```664```

701 665 

702## Retrieving a previous model response

703 

704If you have a previous response's ID, you can retrieve the content of the response.

705 

706```python customLanguage="pythonXAI"666```python customLanguage="pythonXAI"

707import os667import os

708from xai_sdk import Client668from xai_sdk import Client


714 timeout=3600,674 timeout=3600,

715)675)

716 676 

717response = client.chat.get_stored_completion("<The previous response's id>")677chat = client.chat.create(model="grok-4.7", store_messages=True, use_encrypted_content=True)

678chat.append(system("You are Grok, an AI agent built to answer helpful questions."))

679chat.append(user("How big is the universe?"))

680response = chat.sample()

718 681 

719print(response)682print(response)

683 

684# The response ID that can be used to continue the conversation later

685 

686print(response.id)

687 

688# New steps

689 

690chat.append(response) ## Append the response and the SDK will automatically add the outputs from response to message history

691 

692chat.append(user("How do stars form?"))

693second_response = chat.sample()

694 

695print(second_response)

696 

697# The response ID that can be used to continue the conversation later

698 

699print(second_response.id)

700```

701 

702## Retrieving a previous model response

703 

704If you have a previous response's ID, you can retrieve the content of the response.

705 

706```javascript customLanguage="javascriptAISDK"

707// Note: The Vercel AI SDK does not provide a method to retrieve previous responses.

708// Use the OpenAI SDK as shown above for this functionality.

709 

710import OpenAI from "openai";

711 

712const client = new OpenAI({

713 apiKey: "<api key>",

714 baseURL: "https://api.x.ai/v1",

715 timeout: 360000,

716});

717 

718const response = await client.responses.retrieve("<The previous response's id>");

719 

720console.log(response);

720```721```

721 722 

722```python customLanguage="pythonOpenAISDK"723```python customLanguage="pythonOpenAISDK"


749console.log(response);750console.log(response);

750```751```

751 752 

752```javascript customLanguage="javascriptAISDK"

753// Note: The Vercel AI SDK does not provide a method to retrieve previous responses.

754// Use the OpenAI SDK as shown above for this functionality.

755 

756import OpenAI from "openai";

757 

758const client = new OpenAI({

759 apiKey: "<api key>",

760 baseURL: "https://api.x.ai/v1",

761 timeout: 360000,

762});

763 

764const response = await client.responses.retrieve("<The previous response's id>");

765 

766console.log(response);

767```

768 

769```bash753```bash

770curl https://api.x.ai/v1/responses/{response_id} \754curl https://api.x.ai/v1/responses/{response_id} \

771 -H "Content-Type: application/json" \755 -H "Content-Type: application/json" \


773 -m 3600757 -m 3600

774```758```

775 759 

776## Delete a model response

777 

778If you no longer want to store the previous model response, you can delete it.

779 

780```python customLanguage="pythonXAI"760```python customLanguage="pythonXAI"

781import os761import os

782from xai_sdk import Client762from xai_sdk import Client


788 timeout=3600,768 timeout=3600,

789)769)

790 770 

791response = client.chat.delete_stored_completion("<The previous response's id>")771response = client.chat.get_stored_completion("<The previous response's id>")

772 

792print(response)773print(response)

793```774```

794 775 

776## Delete a model response

777 

778If you no longer want to store the previous model response, you can delete it.

779 

780```javascript customLanguage="javascriptAISDK"

781// Note: The Vercel AI SDK does not provide a method to delete previous responses.

782// Use the OpenAI SDK as shown above for this functionality.

783 

784import OpenAI from "openai";

785 

786const client = new OpenAI({

787 apiKey: "<api key>",

788 baseURL: "https://api.x.ai/v1",

789 timeout: 360000,

790});

791 

792const response = await client.responses.delete("<The previous response's id>");

793 

794console.log(response);

795```

796 

795```python customLanguage="pythonOpenAISDK"797```python customLanguage="pythonOpenAISDK"

796import os798import os

797import httpx799import httpx


822console.log(response);824console.log(response);

823```825```

824 826 

825```javascript customLanguage="javascriptAISDK"

826// Note: The Vercel AI SDK does not provide a method to delete previous responses.

827// Use the OpenAI SDK as shown above for this functionality.

828 

829import OpenAI from "openai";

830 

831const client = new OpenAI({

832 apiKey: "<api key>",

833 baseURL: "https://api.x.ai/v1",

834 timeout: 360000,

835});

836 

837const response = await client.responses.delete("<The previous response's id>");

838 

839console.log(response);

840```

841 

842```bash827```bash

843curl -X DELETE https://api.x.ai/v1/responses/{response_id} \828curl -X DELETE https://api.x.ai/v1/responses/{response_id} \

844 -H "Content-Type: application/json" \829 -H "Content-Type: application/json" \

845 -H "Authorization: Bearer $XAI_API_KEY" \830 -H "Authorization: Bearer $XAI_API_KEY" \

846 -m 3600831 -m 3600

847```832```

833 

834```python customLanguage="pythonXAI"

835import os

836from xai_sdk import Client

837from xai_sdk.chat import user, system

838 

839client = Client(

840 api_key=os.getenv("XAI_API_KEY"),

841 management_api_key=os.getenv("XAI_MANAGEMENT_API_KEY"),

842 timeout=3600,

843)

844 

845response = client.chat.delete_stored_completion("<The previous response's id>")

846print(response)

847```

Details

21 21 

22To use Realtime Multi-agent Research, specify `grok-4.20-multi-agent` as the model name in your API requests. This model is optimized for orchestrating multiple agents that collaborate on research tasks.22To use Realtime Multi-agent Research, specify `grok-4.20-multi-agent` as the model name in your API requests. This model is optimized for orchestrating multiple agents that collaborate on research tasks.

23 23 

24```python customLanguage="pythonXAI" highlightedLines="9"24```typescript customLanguage="javascriptAISDK" highlightedLines="5"

25import os25import { xai } from "@ai-sdk/xai";

26 26import { generateText } from "ai";

27from xai_sdk import Client

28from xai_sdk.chat import user

29from xai_sdk.tools import web_search, x_search

30 

31client = Client(api_key=os.getenv("XAI_API_KEY"))

32chat = client.chat.create(

33 model="grok-4.20-multi-agent",

34 tools=[web_search(), x_search()],

35 include=["verbose_streaming"],

36)

37 

38chat.append(user("Research the latest breakthroughs in quantum computing and summarize the key findings."))

39 27 

40is_thinking = True28const { text } = await generateText({

41for response, chunk in chat.stream():29 model: xai.responses("grok-4.20-multi-agent"),

42 if response.usage.reasoning_tokens and is_thinking:30 prompt:

43 print(f"\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)31 "Research the latest breakthroughs in quantum computing and summarize the key findings.",

44 if chunk.content and is_thinking:32 tools: {

45 print("\n\nFinal Response:")33 web_search: xai.tools.webSearch(),

46 is_thinking = False34 x_search: xai.tools.xSearch(),

47 if chunk.content and not is_thinking:35 },

48 print(chunk.content, end="", flush=True)36});

49 37 

50print("\n\nUsage:")38console.log(text);

51print(response.usage)

52```39```

53 40 

54```python customLanguage="pythonOpenAISDK" highlightedLines="10"41```python customLanguage="pythonOpenAISDK" highlightedLines="10"


122}'109}'

123```110```

124 111 

125```typescript customLanguage="javascriptAISDK" highlightedLines="5"112```python customLanguage="pythonXAI" highlightedLines="9"

126import { xai } from "@ai-sdk/xai";113import os

127import { generateText } from "ai";

128 114 

129const { text } = await generateText({115from xai_sdk import Client

130 model: xai.responses("grok-4.20-multi-agent"),116from xai_sdk.chat import user

131 prompt:117from xai_sdk.tools import web_search, x_search

132 "Research the latest breakthroughs in quantum computing and summarize the key findings.",

133 tools: {

134 web_search: xai.tools.webSearch(),

135 x_search: xai.tools.xSearch(),

136 },

137});

138 118 

139console.log(text);119client = Client(api_key=os.getenv("XAI_API_KEY"))

120chat = client.chat.create(

121 model="grok-4.20-multi-agent",

122 tools=[web_search(), x_search()],

123 include=["verbose_streaming"],

124)

125 

126chat.append(user("Research the latest breakthroughs in quantum computing and summarize the key findings."))

127 

128is_thinking = True

129for response, chunk in chat.stream():

130 if response.usage.reasoning_tokens and is_thinking:

131 print(f"\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

132 if chunk.content and is_thinking:

133 print("\n\nFinal Response:")

134 is_thinking = False

135 if chunk.content and not is_thinking:

136 print(chunk.content, end="", flush=True)

137 

138print("\n\nUsage:")

139print(response.usage)

140```140```

141 141 

142## How Multi-agent Works142## How Multi-agent Works


176 176 

177### 4-Agent Setup177### 4-Agent Setup

178 178 

179```python customLanguage="pythonXAI" highlightedLines="8,9"179```typescript customLanguage="javascriptAISDK" highlightedLines="5,8"

180import os180import { xai } from "@ai-sdk/xai";

181 181import { generateText } from "ai";

182from xai_sdk import Client

183from xai_sdk.chat import user

184 182 

185client = Client(api_key=os.getenv("XAI_API_KEY"))183const { text } = await generateText({

186chat = client.chat.create(184 model: xai.responses("grok-4.20-multi-agent"),

187 model="grok-4.20-multi-agent",185 prompt: "What are the key differences between TCP and UDP?",

188 agent_count=4,186 providerOptions: {

189)187 xai: { reasoningEffort: "low" },

188 },

189});

190 190 

191chat.append(user("What are the key differences between TCP and UDP?"))191console.log(text);

192for response, chunk in chat.stream():

193 if chunk.content:

194 print(chunk.content, end="", flush=True)

195```192```

196 193 

197```python customLanguage="pythonOpenAISDK" highlightedLines="10,11"194```python customLanguage="pythonOpenAISDK" highlightedLines="10,11"


256}'253}'

257```254```

258 255 

259```typescript customLanguage="javascriptAISDK" highlightedLines="5,8"

260import { xai } from "@ai-sdk/xai";

261import { generateText } from "ai";

262 

263const { text } = await generateText({

264 model: xai.responses("grok-4.20-multi-agent"),

265 prompt: "What are the key differences between TCP and UDP?",

266 providerOptions: {

267 xai: { reasoningEffort: "low" },

268 },

269});

270 

271console.log(text);

272```

273 

274### 16-Agent Setup

275 

276```python customLanguage="pythonXAI" highlightedLines="8,9"256```python customLanguage="pythonXAI" highlightedLines="8,9"

277import os257import os

278 258 


282client = Client(api_key=os.getenv("XAI_API_KEY"))262client = Client(api_key=os.getenv("XAI_API_KEY"))

283chat = client.chat.create(263chat = client.chat.create(

284 model="grok-4.20-multi-agent",264 model="grok-4.20-multi-agent",

285 agent_count=16,265 agent_count=4,

286)266)

287 267 

288chat.append(user("Analyze the design trade-offs in modern programming languages: compare Rust's ownership model, Go's simplicity philosophy, and Haskell's pure functional approach. Cover memory safety, concurrency, developer productivity, and ecosystem maturity."))268chat.append(user("What are the key differences between TCP and UDP?"))

289for response, chunk in chat.stream():269for response, chunk in chat.stream():

290 if chunk.content:270 if chunk.content:

291 print(chunk.content, end="", flush=True)271 print(chunk.content, end="", flush=True)

292```272```

293 273 

274### 16-Agent Setup

275 

276```typescript customLanguage="javascriptAISDK" highlightedLines="5,9"

277import { xai } from "@ai-sdk/xai";

278import { generateText } from "ai";

279 

280const { text } = await generateText({

281 model: xai.responses("grok-4.20-multi-agent"),

282 prompt:

283 "Analyze the design trade-offs in modern programming languages: compare Rust's ownership model, Go's simplicity philosophy, and Haskell's pure functional approach. Cover memory safety, concurrency, developer productivity, and ecosystem maturity.",

284 providerOptions: {

285 xai: { reasoningEffort: "high" },

286 },

287});

288 

289console.log(text);

290```

291 

294```python customLanguage="pythonOpenAISDK" highlightedLines="10,11"292```python customLanguage="pythonOpenAISDK" highlightedLines="10,11"

295import os293import os

296from openai import OpenAI294from openai import OpenAI


353}'351}'

354```352```

355 353 

356```typescript customLanguage="javascriptAISDK" highlightedLines="5,9"354```python customLanguage="pythonXAI" highlightedLines="8,9"

357import { xai } from "@ai-sdk/xai";355import os

358import { generateText } from "ai";

359 356 

360const { text } = await generateText({357from xai_sdk import Client

361 model: xai.responses("grok-4.20-multi-agent"),358from xai_sdk.chat import user

362 prompt:

363 "Analyze the design trade-offs in modern programming languages: compare Rust's ownership model, Go's simplicity philosophy, and Haskell's pure functional approach. Cover memory safety, concurrency, developer productivity, and ecosystem maturity.",

364 providerOptions: {

365 xai: { reasoningEffort: "high" },

366 },

367});

368 359 

369console.log(text);360client = Client(api_key=os.getenv("XAI_API_KEY"))

361chat = client.chat.create(

362 model="grok-4.20-multi-agent",

363 agent_count=16,

364)

365 

366chat.append(user("Analyze the design trade-offs in modern programming languages: compare Rust's ownership model, Go's simplicity philosophy, and Haskell's pure functional approach. Cover memory safety, concurrency, developer productivity, and ecosystem maturity."))

367for response, chunk in chat.stream():

368 if chunk.content:

369 print(chunk.content, end="", flush=True)

370```370```

371 371 

372> [!NOTE]372> [!NOTE]


379 379 

380Multi-agent works without any built-in tools — the agents rely purely on their collective knowledge and reasoning to collaborate on a response.380Multi-agent works without any built-in tools — the agents rely purely on their collective knowledge and reasoning to collaborate on a response.

381 381 

382```python customLanguage="pythonXAI"382```typescript customLanguage="javascriptAISDK"

383import os383import { xai } from "@ai-sdk/xai";

384 384import { generateText } from "ai";

385from xai_sdk import Client

386from xai_sdk.chat import user

387 

388client = Client(api_key=os.getenv("XAI_API_KEY"))

389chat = client.chat.create(

390 model="grok-4.20-multi-agent",

391 include=["verbose_streaming"],

392)

393 

394chat.append(user("Compare the major approaches to distributed consensus in computer science: Paxos, Raft, and Byzantine fault tolerance. Analyze the trade-offs in safety guarantees, performance, and implementation complexity."))

395 385 

396is_thinking = True386const { text } = await generateText({

397for response, chunk in chat.stream():387 model: xai.responses("grok-4.20-multi-agent"),

398 if response.usage.reasoning_tokens and is_thinking:388 prompt:

399 print(f"\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)389 "Compare the major approaches to distributed consensus in computer science: Paxos, Raft, and Byzantine fault tolerance. Analyze the trade-offs in safety guarantees, performance, and implementation complexity.",

400 if chunk.content and is_thinking:390});

401 print("\n\nFinal Response:")

402 is_thinking = False

403 if chunk.content and not is_thinking:

404 print(chunk.content, end="", flush=True)

405 391 

406print("\n\nUsage:")392console.log(text);

407print(response.usage)

408```393```

409 394 

410```python customLanguage="pythonOpenAISDK"395```python customLanguage="pythonOpenAISDK"


466}'451}'

467```452```

468 453 

469```typescript customLanguage="javascriptAISDK"454```python customLanguage="pythonXAI"

470import { xai } from "@ai-sdk/xai";455import os

471import { generateText } from "ai";

472 456 

473const { text } = await generateText({457from xai_sdk import Client

474 model: xai.responses("grok-4.20-multi-agent"),458from xai_sdk.chat import user

475 prompt:

476 "Compare the major approaches to distributed consensus in computer science: Paxos, Raft, and Byzantine fault tolerance. Analyze the trade-offs in safety guarantees, performance, and implementation complexity.",

477});

478 459 

479console.log(text);460client = Client(api_key=os.getenv("XAI_API_KEY"))

461chat = client.chat.create(

462 model="grok-4.20-multi-agent",

463 include=["verbose_streaming"],

464)

465 

466chat.append(user("Compare the major approaches to distributed consensus in computer science: Paxos, Raft, and Byzantine fault tolerance. Analyze the trade-offs in safety guarantees, performance, and implementation complexity."))

467 

468is_thinking = True

469for response, chunk in chat.stream():

470 if response.usage.reasoning_tokens and is_thinking:

471 print(f"\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

472 if chunk.content and is_thinking:

473 print("\n\nFinal Response:")

474 is_thinking = False

475 if chunk.content and not is_thinking:

476 print(chunk.content, end="", flush=True)

477 

478print("\n\nUsage:")

479print(response.usage)

480```480```

481 481 

482### Multi-turn Conversation482### Multi-turn Conversation

Details

46 46 

47The following example sets `reasoning_effort` to `"high"` for a challenging math proof. You can substitute `"low"`, `"medium"`, or (on supported models) `"xhigh"` as needed.47The following example sets `reasoning_effort` to `"high"` for a challenging math proof. You can substitute `"low"`, `"medium"`, or (on supported models) `"xhigh"` as needed.

48 48 

49```python customLanguage="pythonXAI" highlightedLines="13"49```typescript customLanguage="javascriptAISDK" highlightedLines="9"

50import os50import { xai } from '@ai-sdk/xai';

51 51import { generateText } from 'ai';

52from xai_sdk import Client

53from xai_sdk.chat import system, user

54 

55client = Client(

56 api_key=os.getenv("XAI_API_KEY"),

57 timeout=3600,

58)

59 

60chat = client.chat.create(

61 model="grok-4.7",

62 reasoning_effort="high",

63 messages=[system("You are a highly intelligent AI assistant.")],

64)

65chat.append(user("Find all prime numbers p such that p^2 + 2 is also prime. Prove your answer."))

66 52 

67response = chat.sample()53const result = await generateText({

54 model: xai.responses('grok-4.7'),

55 system: 'You are a highly intelligent AI assistant.',

56 prompt: 'Find all prime numbers p such that p^2 + 2 is also prime. Prove your answer.',

57 providerOptions: {

58 xai: { reasoningEffort: 'high' },

59 },

60});

68 61 

69print("Final Response:")62console.log('Final Response:', result.text);

70print(response.content)

71```63```

72 64 

73```python customLanguage="pythonOpenAISDK" highlightedLines="13"65```python customLanguage="pythonOpenAISDK" highlightedLines="13"


97print(text)89print(text)

98```90```

99 91 

100```typescript customLanguage="javascriptAISDK" highlightedLines="9"

101import { xai } from '@ai-sdk/xai';

102import { generateText } from 'ai';

103 

104const result = await generateText({

105 model: xai.responses('grok-4.7'),

106 system: 'You are a highly intelligent AI assistant.',

107 prompt: 'Find all prime numbers p such that p^2 + 2 is also prime. Prove your answer.',

108 providerOptions: {

109 xai: { reasoningEffort: 'high' },

110 },

111});

112 

113console.log('Final Response:', result.text);

114```

115 

116```bash customLanguage="bash" highlightedLines="7"92```bash customLanguage="bash" highlightedLines="7"

117curl https://api.x.ai/v1/responses \93curl https://api.x.ai/v1/responses \

118 -H "Content-Type: application/json" \94 -H "Content-Type: application/json" \


134}'110}'

135```111```

136 112 

113```python customLanguage="pythonXAI" highlightedLines="13"

114import os

115 

116from xai_sdk import Client

117from xai_sdk.chat import system, user

118 

119client = Client(

120 api_key=os.getenv("XAI_API_KEY"),

121 timeout=3600,

122)

123 

124chat = client.chat.create(

125 model="grok-4.7",

126 reasoning_effort="high",

127 messages=[system("You are a highly intelligent AI assistant.")],

128)

129chat.append(user("Find all prime numbers p such that p^2 + 2 is also prime. Prove your answer."))

130 

131response = chat.sample()

132 

133print("Final Response:")

134print(response.content)

135```

136 

137### Multi-agent model137### Multi-agent model

138 138 

139For `grok-4.20-multi-agent`, the `reasoning.effort` parameter controls **how many agents** collaborate on a request rather than reasoning depth. See the [Multi Agent](/developers/model-capabilities/text/multi-agent) documentation for details.139For `grok-4.20-multi-agent`, the `reasoning.effort` parameter controls **how many agents** collaborate on a request rather than reasoning depth. See the [Multi Agent](/developers/model-capabilities/text/multi-agent) documentation for details.


151 151 

152For `grok-4.7`, we expose summarizations of the model's internal reasoning. Here's an example of how to stream the reasoning summary deltas alongside the final response:152For `grok-4.7`, we expose summarizations of the model's internal reasoning. Here's an example of how to stream the reasoning summary deltas alongside the final response:

153 153 

154```python customLanguage="pythonXAI"154```typescript customLanguage="javascriptAISDK"

155import os155import { xai } from '@ai-sdk/xai';

156 156import { streamText } from 'ai';

157from xai_sdk import Client

158from xai_sdk.chat import system, user

159 

160client = Client(

161 api_key=os.getenv("XAI_API_KEY"),

162 timeout=3600, # Override default timeout with longer timeout for reasoning models

163)

164 

165chat = client.chat.create(

166 model="grok-4.7",

167 messages=[system("You are a highly intelligent AI assistant.")],

168)

169chat.append(user("A projectile is launched at 30 m/s at 37° above horizontal from a 45 m cliff. Find its speed on impact. (g=10 m/s²)"))

170 157 

171content_started = False158const result = streamText({

159 model: xai.responses('grok-4.7'),

160 system: 'You are a highly intelligent AI assistant.',

161 prompt: 'A projectile is launched at 30 m/s at 37° above horizontal from a 45 m cliff. Find its speed on impact. (g=10 m/s²)'

162});

172 163 

173print("\n\n--------- Reasoning ---------", flush=True)164console.log("\n\n--------- Reasoning ---------")

174 165 

175latest_response = None166for await (const part of result.fullStream) {

176for response, chunk in chat.stream():167 if (part.type === 'reasoning-delta') {

177 if chunk.reasoning_content:168 process.stdout.write(part.text);

178 print(chunk.reasoning_content, end="", flush=True)169 }

170}

179```171```

180 172 

181```python customLanguage="pythonOpenAISDK"173```python customLanguage="pythonOpenAISDK"


204 print(event.delta, end="", flush=True)196 print(event.delta, end="", flush=True)

205```197```

206 198 

207```typescript customLanguage="javascriptAISDK"

208import { xai } from '@ai-sdk/xai';

209import { streamText } from 'ai';

210 

211const result = streamText({

212 model: xai.responses('grok-4.7'),

213 system: 'You are a highly intelligent AI assistant.',

214 prompt: 'A projectile is launched at 30 m/s at 37° above horizontal from a 45 m cliff. Find its speed on impact. (g=10 m/s²)'

215});

216 

217console.log("\n\n--------- Reasoning ---------")

218 

219for await (const part of result.fullStream) {

220 if (part.type === 'reasoning-delta') {

221 process.stdout.write(part.text);

222 }

223}

224```

225 

226```bash customLanguage="bash"199```bash customLanguage="bash"

227curl https://api.x.ai/v1/responses \200curl https://api.x.ai/v1/responses \

228 -H "Content-Type: application/json" \201 -H "Content-Type: application/json" \


244}'217}'

245```218```

246 219 

220```python customLanguage="pythonXAI"

221import os

222 

223from xai_sdk import Client

224from xai_sdk.chat import system, user

225 

226client = Client(

227 api_key=os.getenv("XAI_API_KEY"),

228 timeout=3600, # Override default timeout with longer timeout for reasoning models

229)

230 

231chat = client.chat.create(

232 model="grok-4.7",

233 messages=[system("You are a highly intelligent AI assistant.")],

234)

235chat.append(user("A projectile is launched at 30 m/s at 37° above horizontal from a 45 m cliff. Find its speed on impact. (g=10 m/s²)"))

236 

237content_started = False

238 

239print("\n\n--------- Reasoning ---------", flush=True)

240 

241latest_response = None

242for response, chunk in chat.stream():

243 if chunk.reasoning_content:

244 print(chunk.reasoning_content, end="", flush=True)

245```

246 

247### Sample Output247### Sample Output

248 248 

249```output249```output

Details

15> When using streaming output with reasoning models, you might want to **manually override request15> When using streaming output with reasoning models, you might want to **manually override request

16> timeout** to avoid prematurely closing connection.16> timeout** to avoid prematurely closing connection.

17 17 

18```pythonXAI18```javascriptAISDK

19import os19import { xai } from '@ai-sdk/xai';

20 20import { streamText } from 'ai';

21from xai_sdk import Client

22from xai_sdk.chat import user, system

23 

24client = Client(

25 api_key=os.getenv('XAI_API_KEY'),

26 timeout=3600,

27)

28 

29chat = client.chat.create(model="grok-4.7")

30chat.append(

31 system("You are Grok, a helpful and useful AI built by xAI."),

32)

33chat.append(

34 user("Explain how neural networks learn in two sentences.")

35)

36 21 

37for response, chunk in chat.stream():22const result = streamText({

38 print(chunk.content, end="", flush=True)23 model: xai.responses('grok-4.7'),

24 system:

25 "You are Grok, a helpful and useful AI built by xAI.",

26 prompt: 'Explain how neural networks learn in two sentences.',

27});

39 28 

40print()29for await (const chunk of result.textStream) {

41print(response.content)30 process.stdout.write(chunk);

31}

42```32```

43 33 

44```pythonOpenAISDK34```pythonOpenAISDK


94}84}

95```85```

96 86 

97```javascriptAISDK

98import { xai } from '@ai-sdk/xai';

99import { streamText } from 'ai';

100 

101const result = streamText({

102 model: xai.responses('grok-4.7'),

103 system:

104 "You are Grok, a helpful and useful AI built by xAI.",

105 prompt: 'Explain how neural networks learn in two sentences.',

106});

107 

108for await (const chunk of result.textStream) {

109 process.stdout.write(chunk);

110}

111```

112 

113```bash87```bash

114curl https://api.x.ai/v1/chat/completions \\88curl https://api.x.ai/v1/chat/completions \\

115-H "Content-Type: application/json" \\89-H "Content-Type: application/json" \\


131}'105}'

132```106```

133 107 

108```pythonXAI

109import os

110 

111from xai_sdk import Client

112from xai_sdk.chat import user, system

113 

114client = Client(

115 api_key=os.getenv('XAI_API_KEY'),

116 timeout=3600,

117)

118 

119chat = client.chat.create(model="grok-4.7")

120chat.append(

121 system("You are Grok, a helpful and useful AI built by xAI."),

122)

123chat.append(

124 user("Explain how neural networks learn in two sentences.")

125)

126 

127for response, chunk in chat.stream():

128 print(chunk.content, end="", flush=True)

129 

130print()

131print(response.content)

132```

133 

134You'll get the event streams like these:134You'll get the event streams like these:

135 135 

136```json136```json

Details

226 226 

227Use the structured outputs feature of the SDK to parse the invoice.227Use the structured outputs feature of the SDK to parse the invoice.

228 228 

229```pythonXAI229```javascriptAISDK

230import os230import { xai } from '@ai-sdk/xai';

231from datetime import date231import { generateText, Output } from 'ai';

232from enum import Enum232import { z } from 'zod';

233 

234from pydantic import BaseModel, Field

235 

236from xai_sdk import Client

237from xai_sdk.chat import system, user

238 

239# Pydantic Schemas

240 

241class Currency(str, Enum):

242 USD = "USD"

243 EUR = "EUR"

244 GBP = "GBP"

245 

246class LineItem(BaseModel):

247 description: str = Field(description="Description of the item or service")

248 quantity: int = Field(description="Number of units", ge=1)

249 unit_price: float = Field(description="Price per unit", ge=0)

250 

251class Address(BaseModel):

252 street: str = Field(description="Street address")

253 city: str = Field(description="City")

254 postal_code: str = Field(description="Postal/ZIP code")

255 country: str = Field(description="Country")

256 

257class Invoice(BaseModel):

258 vendor_name: str = Field(description="Name of the vendor")

259 vendor_address: Address = Field(description="Vendor's address")

260 invoice_number: str = Field(description="Unique invoice identifier")

261 invoice_date: date = Field(description="Date the invoice was issued")

262 line_items: list[LineItem] = Field(description="List of purchased items/services")

263 total_amount: float = Field(description="Total amount due", ge=0)

264 currency: Currency = Field(description="Currency of the invoice")

265 

266client = Client(api_key=os.getenv("XAI_API_KEY"))

267chat = client.chat.create(model="grok-4.7")

268 

269chat.append(system("Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format."))

270chat.append(

271user("""

272Vendor: Acme Corp, 123 Main St, Springfield, IL 62704

273Invoice Number: INV-2025-001

274Date: 2025-02-10

275Items: - Widget A, 5 units, $10.00 each - Widget B, 2 units, $15.00 each

276Total: $80.00 USD

277""")

278)

279 233 

280# The parse method returns a tuple of the full response object as well as the parsed pydantic object.234const CurrencyEnum = z.enum(['USD', 'EUR', 'GBP']);

281 235 

282response, invoice = chat.parse(Invoice)236const LineItemSchema = z.object({

283assert isinstance(invoice, Invoice)237 description: z.string().describe('Description of the item or service'),

238 quantity: z.number().int().min(1).describe('Number of units'),

239 unit_price: z.number().min(0).describe('Price per unit'),

240});

284 241 

285# Can access fields of the parsed invoice object directly242const AddressSchema = z.object({

243 street: z.string().describe('Street address'),

244 city: z.string().describe('City'),

245 postal_code: z.string().describe('Postal/ZIP code'),

246 country: z.string().describe('Country'),

247});

286 248 

287print(invoice.vendor_name)249const InvoiceSchema = z.object({

288print(invoice.invoice_number)250 vendor_name: z.string().describe('Name of the vendor'),

289print(invoice.invoice_date)251 vendor_address: AddressSchema.describe("Vendor's address"),

290print(invoice.line_items)252 invoice_number: z.string().describe('Unique invoice identifier'),

291print(invoice.total_amount)253 invoice_date: z.string().date().describe('Date the invoice was issued'),

292print(invoice.currency)254 line_items: z

255 .array(LineItemSchema)

256 .describe('List of purchased items/services'),

257 total_amount: z.number().min(0).describe('Total amount due'),

258 currency: CurrencyEnum.describe('Currency of the invoice'),

259});

293 260 

294# Can also access fields from the raw response object such as the content.261const result = await generateText({

262 model: xai.responses('grok-4.7'),

263 output: Output.object({ schema: InvoiceSchema }),

264 system:

265 'Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format.',

266 prompt: \`

267 Vendor: Acme Corp, 123 Main St, Springfield, IL 62704

268 Invoice Number: INV-2025-001

269 Date: 2025-02-10

270 Items:

295 271 

296# In this case, the content is the JSON schema representation of the parsed invoice object272 - Widget A, 5 units, $10.00 each

273 - Widget B, 2 units, $15.00 each

274 Total: $80.00 USD

275 \`,

276});

297 277 

298print(response.content)278console.log(result._output);

299```279```

300 280 

301```pythonOpenAISDK281```pythonOpenAISDK


416console.log(invoice);396console.log(invoice);

417```397```

418 398 

419```javascriptAISDK399```pythonXAI

420import { xai } from '@ai-sdk/xai';400import os

421import { generateText, Output } from 'ai';401from datetime import date

422import { z } from 'zod';402from enum import Enum

423 403 

424const CurrencyEnum = z.enum(['USD', 'EUR', 'GBP']);404from pydantic import BaseModel, Field

425 405 

426const LineItemSchema = z.object({406from xai_sdk import Client

427 description: z.string().describe('Description of the item or service'),407from xai_sdk.chat import system, user

428 quantity: z.number().int().min(1).describe('Number of units'),

429 unit_price: z.number().min(0).describe('Price per unit'),

430});

431 408 

432const AddressSchema = z.object({409# Pydantic Schemas

433 street: z.string().describe('Street address'),

434 city: z.string().describe('City'),

435 postal_code: z.string().describe('Postal/ZIP code'),

436 country: z.string().describe('Country'),

437});

438 410 

439const InvoiceSchema = z.object({411class Currency(str, Enum):

440 vendor_name: z.string().describe('Name of the vendor'),412 USD = "USD"

441 vendor_address: AddressSchema.describe("Vendor's address"),413 EUR = "EUR"

442 invoice_number: z.string().describe('Unique invoice identifier'),414 GBP = "GBP"

443 invoice_date: z.string().date().describe('Date the invoice was issued'),

444 line_items: z

445 .array(LineItemSchema)

446 .describe('List of purchased items/services'),

447 total_amount: z.number().min(0).describe('Total amount due'),

448 currency: CurrencyEnum.describe('Currency of the invoice'),

449});

450 415 

451const result = await generateText({416class LineItem(BaseModel):

452 model: xai.responses('grok-4.7'),417 description: str = Field(description="Description of the item or service")

453 output: Output.object({ schema: InvoiceSchema }),418 quantity: int = Field(description="Number of units", ge=1)

454 system:419 unit_price: float = Field(description="Price per unit", ge=0)

455 'Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format.',

456 prompt: \`

457 Vendor: Acme Corp, 123 Main St, Springfield, IL 62704

458 Invoice Number: INV-2025-001

459 Date: 2025-02-10

460 Items:

461 420 

462 - Widget A, 5 units, $10.00 each421class Address(BaseModel):

463 - Widget B, 2 units, $15.00 each422 street: str = Field(description="Street address")

464 Total: $80.00 USD423 city: str = Field(description="City")

465 \`,424 postal_code: str = Field(description="Postal/ZIP code")

466});425 country: str = Field(description="Country")

467 426 

468console.log(result._output);427class Invoice(BaseModel):

428 vendor_name: str = Field(description="Name of the vendor")

429 vendor_address: Address = Field(description="Vendor's address")

430 invoice_number: str = Field(description="Unique invoice identifier")

431 invoice_date: date = Field(description="Date the invoice was issued")

432 line_items: list[LineItem] = Field(description="List of purchased items/services")

433 total_amount: float = Field(description="Total amount due", ge=0)

434 currency: Currency = Field(description="Currency of the invoice")

435 

436client = Client(api_key=os.getenv("XAI_API_KEY"))

437chat = client.chat.create(model="grok-4.7")

438 

439chat.append(system("Given a raw invoice, carefully analyze the text and extract the invoice data into JSON format."))

440chat.append(

441user("""

442Vendor: Acme Corp, 123 Main St, Springfield, IL 62704

443Invoice Number: INV-2025-001

444Date: 2025-02-10

445Items: - Widget A, 5 units, $10.00 each - Widget B, 2 units, $15.00 each

446Total: $80.00 USD

447""")

448)

449 

450# The parse method returns a tuple of the full response object as well as the parsed pydantic object.

451 

452response, invoice = chat.parse(Invoice)

453assert isinstance(invoice, Invoice)

454 

455# Can access fields of the parsed invoice object directly

456 

457print(invoice.vendor_name)

458print(invoice.invoice_number)

459print(invoice.invoice_date)

460print(invoice.line_items)

461print(invoice.total_amount)

462print(invoice.currency)

463 

464# Can also access fields from the raw response object such as the content.

465 

466# In this case, the content is the JSON schema representation of the parsed invoice object

467 

468print(response.content)

469```469```

470 470 

471### Step 4: Type-safe Output471### Step 4: Type-safe Output


530});530});

531```531```

532 532 

533```python customLanguage="pythonXAI"

534import os

535from pydantic import BaseModel, Field

536 

537from xai_sdk import Client

538from xai_sdk.chat import user

539from xai_sdk.tools import web_search

540 

541# ProofInfo schema defined above

542 

543client = Client(api_key=os.getenv("XAI_API_KEY"))

544chat = client.chat.create(

545 model="grok-4.7",

546 tools=[web_search()],

547)

548 

549chat.append(user("Find the latest machine-checked proof of the four color theorem."))

550 

551response, proof = chat.parse(ProofInfo)

552 

553print(f"Name: {proof.name}")

554print(f"Authors: {proof.authors}")

555print(f"Year: {proof.year}")

556print(f"Summary: {proof.summary}")

557```

558 

559```python customLanguage="pythonOpenAISDK"533```python customLanguage="pythonOpenAISDK"

560import os534import os

561from openai import OpenAI535from openai import OpenAI


628}602}

629```603```

630 604 

605```python customLanguage="pythonXAI"

606import os

607from pydantic import BaseModel, Field

608 

609from xai_sdk import Client

610from xai_sdk.chat import user

611from xai_sdk.tools import web_search

612 

613# ProofInfo schema defined above

614 

615client = Client(api_key=os.getenv("XAI_API_KEY"))

616chat = client.chat.create(

617 model="grok-4.7",

618 tools=[web_search()],

619)

620 

621chat.append(user("Find the latest machine-checked proof of the four color theorem."))

622 

623response, proof = chat.parse(ProofInfo)

624 

625print(f"Name: {proof.name}")

626print(f"Authors: {proof.authors}")

627print(f"Year: {proof.year}")

628print(f"Summary: {proof.summary}")

629```

630 

631### Example: Client-side Tools with Structured Output631### Example: Client-side Tools with Structured Output

632 632 

633This example uses a client-side function tool to compute Collatz sequence steps and returns the result in a structured format:633This example uses a client-side function tool to compute Collatz sequence steps and returns the result in a structured format:


652};652};

653```653```

654 654 

655```python customLanguage="pythonXAI"

656import os

657import json

658from pydantic import BaseModel, Field

659 

660from xai_sdk import Client

661from xai_sdk.chat import tool, tool_result, user

662 

663# CollatzResult schema defined above

664 

665def collatz_steps(n: int) -> int:

666 """Returns the number of steps for n to reach 1 in the Collatz sequence."""

667 steps = 0

668 while n != 1:

669 n = n // 2 if n % 2 == 0 else 3 * n + 1

670 steps += 1

671 return steps

672 

673collatz_tool = tool(

674 name="collatz_steps",

675 description="Compute the number of steps for a number to reach 1 in the Collatz sequence",

676 parameters={

677 "type": "object",

678 "properties": {

679 "n": {"type": "integer", "description": "The starting number"},

680 },

681 "required": ["n"],

682 },

683)

684 

685client = Client(api_key=os.getenv("XAI_API_KEY"))

686chat = client.chat.create(

687 model="grok-4.7",

688 tools=[collatz_tool],

689)

690 

691chat.append(user("Use the collatz_steps tool to find how many steps it takes for 20250709 to reach 1."))

692 

693# Handle tool calls until we get a final response

694while True:

695 response = chat.sample()

696

697 if not response.tool_calls:

698 break

699

700 chat.append(response)

701 for tc in response.tool_calls:

702 args = json.loads(tc.function.arguments)

703 result = collatz_steps(args["n"])

704 chat.append(tool_result(str(result)))

705 

706# Parse the final response into structured output

707response, result = chat.parse(CollatzResult)

708 

709print(f"Starting number: {result.starting_number}")

710print(f"Steps to reach 1: {result.steps}")

711```

712 

713```python customLanguage="pythonOpenAISDK"655```python customLanguage="pythonOpenAISDK"

714import os656import os

715import json657import json


872console.log("Steps to reach 1:", result.steps);814console.log("Steps to reach 1:", result.steps);

873```815```

874 816 

817```python customLanguage="pythonXAI"

818import os

819import json

820from pydantic import BaseModel, Field

821 

822from xai_sdk import Client

823from xai_sdk.chat import tool, tool_result, user

824 

825# CollatzResult schema defined above

826 

827def collatz_steps(n: int) -> int:

828 """Returns the number of steps for n to reach 1 in the Collatz sequence."""

829 steps = 0

830 while n != 1:

831 n = n // 2 if n % 2 == 0 else 3 * n + 1

832 steps += 1

833 return steps

834 

835collatz_tool = tool(

836 name="collatz_steps",

837 description="Compute the number of steps for a number to reach 1 in the Collatz sequence",

838 parameters={

839 "type": "object",

840 "properties": {

841 "n": {"type": "integer", "description": "The starting number"},

842 },

843 "required": ["n"],

844 },

845)

846 

847client = Client(api_key=os.getenv("XAI_API_KEY"))

848chat = client.chat.create(

849 model="grok-4.7",

850 tools=[collatz_tool],

851)

852 

853chat.append(user("Use the collatz_steps tool to find how many steps it takes for 20250709 to reach 1."))

854 

855# Handle tool calls until we get a final response

856while True:

857 response = chat.sample()

858

859 if not response.tool_calls:

860 break

861

862 chat.append(response)

863 for tc in response.tool_calls:

864 args = json.loads(tc.function.arguments)

865 result = collatz_steps(args["n"])

866 chat.append(tool_result(str(result)))

867 

868# Parse the final response into structured output

869response, result = chat.parse(CollatzResult)

870 

871print(f"Starting number: {result.starting_number}")

872print(f"Steps to reach 1: {result.steps}")

873```

874 

875## Alternative: Using `response_format` with `sample()` or `stream()`875## Alternative: Using `response_format` with `sample()` or `stream()`

876 876 

877When using the xAI Python SDK, there's an alternative way to retrieve structured outputs. Instead of using the `parse()` method, you can pass your Pydantic model directly to the `response_format` parameter when creating a chat, and then use `sample()` or `stream()` to get the response.877When using the xAI Python SDK, there's an alternative way to retrieve structured outputs. Instead of using the `parse()` method, you can pass your Pydantic model directly to the `response_format` parameter when creating a chat, and then use `sample()` or `stream()` to get the response.

Details

6 6 

7You can provide the source video as a public URL, a base64-encoded data URI, or a `file_id` from the [Files API](/developers/files). See [Imagine → Files API Integration](/developers/model-capabilities/imagine/files/inputs) for using `file_id` inputs.7You can provide the source video as a public URL, a base64-encoded data URI, or a `file_id` from the [Files API](/developers/files). See [Imagine → Files API Integration](/developers/model-capabilities/imagine/files/inputs) for using `file_id` inputs.

8 8 

9> [!WARNING]9| Requirement | Value |

10|-------------|-------|

11| Model | `grok-imagine-video` |

12| Source video | `.mp4` with a supported codec such as H.264, H.265, or AV1 |

13| Source length | Up to **8.7 seconds** |

14| Output | Same duration, aspect ratio, and resolution as the source, capped at **720p** |

15| `duration`, `aspect_ratio`, `resolution` | Not supported |

10 16 

11The demo below shows video editing in action. `grok-imagine-video` delivers high-fidelity edits with strong scene preservation, modifying only what you ask for while keeping the rest of the video intact:17`grok-imagine-video` applies the change you describe and keeps everything else as close to the source as it can. Say what must stay the same, as these prompts do:

12 18 

13In the Vercel AI SDK, video editing is triggered by setting `providerOptions.xai.mode` to `"edit-video"` and passing `providerOptions.xai.videoUrl` with a source video URL. The `prompt` describes the desired modifications; `duration`, `aspectRatio`, and `resolution` are ignored because the output inherits these properties from the input video, capped at 720p.19In the Vercel AI SDK, video editing is triggered by setting `providerOptions.xai.mode` to `"edit-video"` and passing `providerOptions.xai.videoUrl` with a source video URL. The `prompt` describes the desired modifications; `duration`, `aspectRatio`, and `resolution` are ignored because the output inherits these properties from the input video, capped at 720p.

14 20 


16 22 

17When you need to apply several edits to the same source video, run requests concurrently. This is useful for branching multiple edits from the same intermediate result.23When you need to apply several edits to the same source video, run requests concurrently. This is useful for branching multiple edits from the same intermediate result.

18 24 

19```python customLanguage="pythonXAI"

20import os

21import asyncio

22import xai_sdk

23 

24async def edit_concurrently():

25 client = xai_sdk.AsyncClient(api_key=os.getenv("XAI_API_KEY"))

26 

27 source_video = "https://data.x.ai/docs/video-generation/portrait-wave.mp4"

28 

29 prompts = [

30 "Give the woman a silver necklace",

31 "Change the color of the woman's outfit to red",

32 "Give the woman a wide-brimmed black hat",

33 ]

34 

35 tasks = [

36 client.video.generate(

37 prompt=prompt,

38 model="grok-imagine-video",

39 video_url=source_video,

40 )

41 for prompt in prompts

42 ]

43 

44 results = await asyncio.gather(*tasks)

45 

46 for prompt, result in zip(prompts, results):

47 print(f"{prompt}: {result.url}")

48 

49asyncio.run(edit_concurrently())

50```

51 

52```javascript customLanguage="javascriptAISDK"25```javascript customLanguage="javascriptAISDK"

53import { xai, type XaiVideoModelOptions } from "@ai-sdk/xai";26import { xai, type XaiVideoModelOptions } from "@ai-sdk/xai";

54import { experimental_generateVideo as generateVideo } from "ai";27import { experimental_generateVideo as generateVideo } from "ai";


98console.log(withScarf.providerMetadata?.xai?.videoUrl);71console.log(withScarf.providerMetadata?.xai?.videoUrl);

99```72```

100 73 

74```python customLanguage="pythonXAI"

75import os

76import asyncio

77import xai_sdk

78 

79async def edit_concurrently():

80 client = xai_sdk.AsyncClient(api_key=os.getenv("XAI_API_KEY"))

81 

82 source_video = "https://media.x.ai/v1/docs/i2v-portrait-wave-4cb4c17c.mp4"

83 

84 prompts = [

85 "Give the woman a silver necklace",

86 "Change the color of the woman's outfit to red",

87 "Give the woman a wide-brimmed black hat",

88 ]

89 

90 tasks = [

91 client.video.generate(

92 prompt=prompt,

93 model="grok-imagine-video",

94 video_url=source_video,

95 )

96 for prompt in prompts

97 ]

98 

99 results = await asyncio.gather(*tasks)

100 

101 for prompt, result in zip(prompts, results):

102 print(f"{prompt}: {result.url}")

103 

104asyncio.run(edit_concurrently())

105```

106 

101## Related107## Related

102 108 

103* [Video Generation](/developers/model-capabilities/video/generation) — Generate videos from text prompts109* [Video Generation](/developers/model-capabilities/video/generation) — Generate videos from text prompts

Details

6 6 

7You can provide the source video as a public URL, a base64-encoded data URI, or a `file_id` from the [Files API](/developers/files). See [Imagine → Files API Integration](/developers/model-capabilities/imagine/files/inputs) for using `file_id` inputs.7You can provide the source video as a public URL, a base64-encoded data URI, or a `file_id` from the [Files API](/developers/files). See [Imagine → Files API Integration](/developers/model-capabilities/imagine/files/inputs) for using `file_id` inputs.

8 8 

9> [!WARNING]9| Requirement | Value |

10|-------------|-------|

11| Model | `grok-imagine-video` |

12| Source video | `.mp4` with a supported codec such as H.264, H.265, or AV1 |

13| Source length | **2–15 seconds** |

14| Extension length (`duration`) | **2–10 seconds**; default 6 |

15| Output | Same aspect ratio and resolution as the source, capped at **720p** |

16| `aspect_ratio`, `resolution` | Not supported |

10 17 

11The `duration` parameter controls the length of the **extended portion only**, not the total output. For example, if your input video is 10 seconds and you set `duration` to 5, the returned video will be 15 seconds long (10s original + 5s extension).18The `duration` parameter controls the length of the **extended portion only**, not the total output. For example, if your input video is 10 seconds and you set `duration` to 5, the returned video will be 15 seconds long (10s original + 5s extension).

12 19 

13```python customLanguage="pythonXAI"20```javascript customLanguage="javascriptAISDK"

14import os21import { xai } from "@ai-sdk/xai";

15import xai_sdk22import { experimental_generateVideo as generateVideo } from "ai";

16 23 

17client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))24const source = await generateVideo({

25 model: xai.video("grok-imagine-video-1.5"),

26 prompt: "A cat sitting on a sunlit windowsill, tail gently swishing.",

27 duration: 5,

28 aspectRatio: "16:9",

29 providerOptions: {

30 xai: {

31 pollTimeoutMs: 600000,

32 },

33 },

34});

18 35 

19response = client.video.extend(36const sourceUrl = source.providerMetadata?.xai?.videoUrl;

20 prompt="The shot pans to an over the shoulder perspective. Calm controlled scene.",

21 model="grok-imagine-video",

22 video_url="<VIDEO_URL>",

23 duration=10,

24)

25 37 

26print(response.url)38const extended = await generateVideo({

39 model: xai.video("grok-imagine-video"),

40 prompt: "The cat turns its head, notices a butterfly, and leaps off.",

41 duration: 6,

42 providerOptions: {

43 xai: {

44 mode: "extend-video",

45 videoUrl: sourceUrl,

46 pollTimeoutMs: 600000,

47 },

48 },

49});

50 

51const extendedVideoUrl = extended.providerMetadata?.xai?.videoUrl;

52console.log(extendedVideoUrl);

27```53```

28 54 

29```python customLanguage="pythonRequests"55```python customLanguage="pythonRequests"


64 time.sleep(5)90 time.sleep(5)

65```91```

66 92 

67```javascript customLanguage="javascriptAISDK"

68import { xai } from "@ai-sdk/xai";

69import { experimental_generateVideo as generateVideo } from "ai";

70 

71const source = await generateVideo({

72 model: xai.video("grok-imagine-video-1.5"),

73 prompt: "A cat sitting on a sunlit windowsill, tail gently swishing.",

74 duration: 5,

75 aspectRatio: "16:9",

76 providerOptions: {

77 xai: {

78 pollTimeoutMs: 600000,

79 },

80 },

81});

82 

83const sourceUrl = source.providerMetadata?.xai?.videoUrl;

84 

85const extended = await generateVideo({

86 model: xai.video("grok-imagine-video"),

87 prompt: "The cat turns its head, notices a butterfly, and leaps off.",

88 duration: 6,

89 providerOptions: {

90 xai: {

91 mode: "extend-video",

92 videoUrl: sourceUrl,

93 pollTimeoutMs: 600000,

94 },

95 },

96});

97 

98const extendedVideoUrl = extended.providerMetadata?.xai?.videoUrl;

99console.log(extendedVideoUrl);

100```

101 

102```bash93```bash

103# Start the video extension request94# Start the video extension request

104REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/extensions \95REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/extensions \


127done118done

128```119```

129 120 

121```python customLanguage="pythonXAI"

122import os

123import xai_sdk

124 

125client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

126 

127response = client.video.extend(

128 prompt="The shot pans to an over the shoulder perspective. Calm controlled scene.",

129 model="grok-imagine-video",

130 video_url="<VIDEO_URL>",

131 duration=10,

132)

133 

134print(response.url)

135```

136 

130Video editing uses the `/v1/videos/edits` endpoint and `client.video.generate(video_url=...)` in the Python SDK. In the AI SDK, set `providerOptions.xai.mode` to `"edit-video"` or `"extend-video"` and pass `providerOptions.xai.videoUrl`. The same asynchronous polling pattern applies to both flows, and the AI SDK returns the SpaceXAI-hosted output URL in `providerMetadata.xai.videoUrl`.137Video editing uses the `/v1/videos/edits` endpoint and `client.video.generate(video_url=...)` in the Python SDK. In the AI SDK, set `providerOptions.xai.mode` to `"edit-video"` or `"extend-video"` and pass `providerOptions.xai.videoUrl`. The same asynchronous polling pattern applies to both flows, and the AI SDK returns the SpaceXAI-hosted output URL in `providerMetadata.xai.videoUrl`.

131 138 

132## Related139## Related

Details

2 2 

3# Video Generation3# Video Generation

4 4 

5Generate videos from text prompts with Grok video models. The API supports configurable duration, aspect ratio, and resolution, and the SDK handles asynchronous polling automatically. On `grok-imagine-video-1.5`, text-to-video supports native 1080p.5Generate videos from text prompts with Grok video models. The API supports configurable duration, aspect ratio, and resolution, and the SDK handles asynchronous polling automatically. On `grok-imagine-video-1.5`, text-to-video supports native 1080p. For simple videos, we recommend `grok-imagine-video-1.5-lite`, the lowest price per second.

6 6 

7> [!NOTE]7> [!NOTE]

8>8>

9> On , text-to-video uses text-to-image then image-to-video under the hood: the model generates a first frame from your prompt, then animates it. You still make a single text-to-video request; the intermediate image is not returned.9> On and , text-to-video uses text-to-image then image-to-video under the hood: the model generates a first frame from your prompt, then animates it. You still make a single text-to-video request; the intermediate image is not returned.

10 

11More examples, for every mode, are on the [Video Overview](/developers/model-capabilities/video/overview).

10 12 

11## Quick Start13## Quick Start

12 14 

13Generate a video with a single API call:15Generate a video with a single API call:

14 16 

15```python customLanguage="pythonXAI"17```javascript customLanguage="javascriptAISDK"

16import os18import { xai } from "@ai-sdk/xai";

17import xai_sdk19import { experimental_generateVideo as generateVideo } from "ai";

18 

19client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

20 20 

21response = client.video.generate(21const result = await generateVideo({

22 prompt="A glowing crystal-powered rocket launching from the red dunes of Mars, ancient alien ruins lighting up in the background as it soars into a sky full of unfamiliar constellations",22 model: xai.video("grok-imagine-video-1.5"),

23 model="grok-imagine-video-1.5",23 prompt: "A glowing crystal-powered rocket launching from the red dunes of Mars, ancient alien ruins lighting up in the background as it soars into a sky full of unfamiliar constellations",

24 duration=10,24 duration: 10,

25 aspect_ratio="16:9",25 aspectRatio: "16:9",

26 resolution="720p",26 providerOptions: {

27)27 xai: { resolution: "720p" },

28 },

29});

28 30 

29print(response.url)31const videoUrl = result.providerMetadata?.xai?.videoUrl;

32console.log(videoUrl);

30```33```

31 34 

32```python customLanguage="pythonRequests"35```python customLanguage="pythonRequests"


63 if data["status"] == "done":66 if data["status"] == "done":

64 print(data["video"]["url"])67 print(data["video"]["url"])

65 break68 break

66 elif data["status"] == "expired":69 elif data["status"] == "failed":

67 print("Request expired")70 print("Video generation failed")

68 break71 break

69 time.sleep(5)72 time.sleep(5)

70```73```

71 74 

72```javascript customLanguage="javascriptAISDK"

73import { xai } from "@ai-sdk/xai";

74import { experimental_generateVideo as generateVideo } from "ai";

75 

76const result = await generateVideo({

77 model: xai.video("grok-imagine-video-1.5"),

78 prompt: "A glowing crystal-powered rocket launching from the red dunes of Mars, ancient alien ruins lighting up in the background as it soars into a sky full of unfamiliar constellations",

79 duration: 10,

80 aspectRatio: "16:9",

81 providerOptions: {

82 xai: { resolution: "720p" },

83 },

84});

85 

86const videoUrl = result.providerMetadata?.xai?.videoUrl;

87console.log(videoUrl);

88```

89 

90```bash75```bash

91# Start the video generation request76# Start the video generation request

92REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \77REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \


108 if [ "$STATUS" = "done" ]; then93 if [ "$STATUS" = "done" ]; then

109 echo "$RESULT" | jq -r '.video.url'94 echo "$RESULT" | jq -r '.video.url'

110 break95 break

111 elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then96 elif [ "$STATUS" = "failed" ]; then

112 echo "Request $STATUS"; echo "$RESULT" | jq .97 echo "Request $STATUS"; echo "$RESULT" | jq .

113 break98 break

114 fi99 fi


116done101done

117```102```

118 103 

104```python customLanguage="pythonXAI"

105import os

106import xai_sdk

107 

108client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

109 

110response = client.video.generate(

111 prompt="A glowing crystal-powered rocket launching from the red dunes of Mars, ancient alien ruins lighting up in the background as it soars into a sky full of unfamiliar constellations",

112 model="grok-imagine-video-1.5",

113 duration=10,

114 aspect_ratio="16:9",

115 resolution="720p",

116)

117 

118print(response.url)

119```

120 

119Video generation is an **asynchronous process** that typically takes up to several minutes to complete. The exact time varies based on:121Video generation is an **asynchronous process** that typically takes up to several minutes to complete. The exact time varies based on:

120 122 

121* **Prompt complexity** — More detailed scenes require additional processing123* **Prompt complexity** — More detailed scenes require additional processing


127 129 

128Use the page that matches the kind of video output you want to create:130Use the page that matches the kind of video output you want to create:

129 131 

130* [Image-to-Video](/developers/model-capabilities/video/image-to-video) — Animate a still image.132* [Reference-to-Video](/developers/model-capabilities/video/reference-to-video): Guide a generated video with reference images; on `grok-imagine-video-1.5`, add voice references and pin exact first, key, or last frames.

131* [Video Editing](/developers/model-capabilities/video/editing) — Modify an existing video.133* [Image-to-Video](/developers/model-capabilities/video/image-to-video): Animate a still image.

132* [Reference-to-Video](/developers/model-capabilities/video/reference-to-video) — Guide a generated video with reference images, and on `grok-imagine-video-1.5` pin exact first or last frames.134* [Video Editing](/developers/model-capabilities/video/editing): Modify an existing video with `grok-imagine-video`.

133* [Video Extension](/developers/model-capabilities/video/extension) — Continue an existing video from its last frame.135* [Video Extension](/developers/model-capabilities/video/extension): Continue an existing video from its last frame with `grok-imagine-video`.

136 

137To compare models, see [Capabilities and specifications](/developers/model-capabilities/video/overview#capabilities-and-specifications).

134 138 

135## How it works139## How it works

136 140 


176|--------|-------------|180|--------|-------------|

177| `pending` | Video is still being generated |181| `pending` | Video is still being generated |

178| `done` | Video is ready |182| `done` | Video is ready |

179| `expired` | Request has expired |

180| `failed` | Video generation failed |183| `failed` | Video generation failed |

181 184 

182Response (when complete):185Response (when complete):


201 204 

202### Duration205### Duration

203 206 

204Control video length with the `duration` parameter. The allowed range is 1–15 seconds.207Set `duration` from 1 to 15 seconds; the default is 8. Reference-to-video on `grok-imagine-video` allows up to 10 seconds.

205 208 

206Video editing does not support custom `duration`. The edited video retains the duration of the original, which is capped at 8.7 seconds.209Video editing does not support custom `duration`. The edited video retains the duration of the original, which is capped at 8.7 seconds.

207 210 


213| `16:9` / `9:16` | Widescreen, mobile, stories (default: `16:9`) |216| `16:9` / `9:16` | Widescreen, mobile, stories (default: `16:9`) |

214| `4:3` / `3:4` | Presentations, portraits |217| `4:3` / `3:4` | Presentations, portraits |

215| `3:2` / `2:3` | Photography |218| `3:2` / `2:3` | Photography |

219| `21:9` | Cinematic widescreen |

220| `5:2` | Web banners, wide headers |

216 221 

217For image-to-video generation, the output defaults to the input image's aspect ratio. If you specify the `aspect_ratio` parameter, it will override this and stretch the image to the desired aspect ratio.222Image-to-video output always matches the input image's aspect ratio; `aspect_ratio` is ignored.

218 223 

219Video editing does not support custom `aspect_ratio` — the output matches the input video's aspect ratio.224Video editing does not support custom `aspect_ratio` — the output matches the input video's aspect ratio.

220 225 


226| `720p` | HD quality |231| `720p` | HD quality |

227| `480p` | Standard definition, faster processing (default) |232| `480p` | Standard definition, faster processing (default) |

228 233 

229**Note:** `1080p` is supported on `grok-imagine-video-1.5` for text-to-video and image-to-video. Reference-to-video is capped at 720p.234**Note:** `grok-imagine-video-1.5` renders `1080p` natively for text-to-video and image-to-video; reference-to-video goes up to `720p`. `grok-imagine-video-1.5-lite` reaches 1080p for text-to-video and image-to-video by upscaling 720p. `grok-imagine-video` supports up to 720p.

230 235 

231Video editing does not support custom `resolution`. The output resolution matches the input video's resolution, capped at 720p (e.g., a 1080p input will be downsized to 720p).236Video editing does not support custom `resolution`. The output resolution matches the input video's resolution, capped at 720p (e.g., a 1080p input will be downsized to 720p).

232 237 

233### Audio238### Audio

234 239 

235> [!WARNING]240Generated videos include an audio track by default, with lip-synced speech on `grok-imagine-video-1.5` and `grok-imagine-video-1.5-lite`. On `grok-imagine-video-1.5`, reference-to-video can give up to 3 speakers a preset voice with `reference_audios`; see [Reference audio](/developers/model-capabilities/video/reference-to-video#reference-audio).

236>

237> Preset voices are generally available. Voice references with your own audio files are available to trusted partners, on request. .

238 

239On `grok-imagine-video-1.5`, [reference-to-video](/developers/model-capabilities/video/reference-to-video#reference-audio) can carry a voice via `reference_audios`. Voices come from the built-in roster and are named by `voice_id`; voice references with your own audio files are available to trusted partners [on request](https://x.ai/contact-sales?interest=imagine):

240 

241| Property | Description |

242|----------|-------------|

243| Source | A preset `voice_id` (e.g. `{"voice_id": "eve"}`), from the same roster as [Text to Speech](/developers/model-capabilities/audio/text-to-speech#voices). Identifiers are case-insensitive |

244| Limit | Max **3** voices per request |

245| Prompt | Reference voices by index: `<AUDIO_0>`, `<AUDIO_1>`, `<AUDIO_2>` |

246 

247Generated videos include an audio track by default. Pass `generate_audio=False` to request a silent video:

248 

249```python customLanguage="pythonXAI"

250import os

251import xai_sdk

252 

253client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

254 241 

255response = client.video.generate(242Pass `generate_audio=False` to request a silent video:

256 prompt="A paper boat drifting down a rain-soaked street",

257 model="grok-imagine-video-1.5",

258 generate_audio=False,

259)

260 

261print(response.url)

262```

263 243 

264```bash244```bash

265curl -X POST https://api.x.ai/v1/videos/generations \245curl -X POST https://api.x.ai/v1/videos/generations \


272 }'252 }'

273```253```

274 254 

275### Example

276 

277```python customLanguage="pythonXAI"255```python customLanguage="pythonXAI"

278import os256import os

279import xai_sdk257import xai_sdk


281client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))259client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

282 260 

283response = client.video.generate(261response = client.video.generate(

284 prompt="Timelapse of a flower blooming in a sunlit garden",262 prompt="A paper boat drifting down a rain-soaked street",

285 model="grok-imagine-video-1.5",263 model="grok-imagine-video-1.5",

286 duration=10,264 generate_audio=False,

287 aspect_ratio="16:9",

288 resolution="720p",

289)265)

290 266 

291print(f"Video URL: {response.url}")267print(response.url)

292print(f"Duration: {response.duration}s")268```

269 

270### Example

271 

272```javascript customLanguage="javascriptAISDK"

273import { xai } from "@ai-sdk/xai";

274import { experimental_generateVideo as generateVideo } from "ai";

275 

276const result = await generateVideo({

277 model: xai.video("grok-imagine-video-1.5"),

278 prompt: "Timelapse of a flower blooming in a sunlit garden",

279 duration: 10,

280 aspectRatio: "16:9",

281 providerOptions: {

282 xai: { resolution: "720p" },

283 },

284});

285 

286const videoUrl = result.providerMetadata?.xai?.videoUrl;

287console.log(videoUrl);

293```288```

294 289 

295```python customLanguage="pythonRequests"290```python customLanguage="pythonRequests"


326 print(f"Video URL: {data['video']['url']}")321 print(f"Video URL: {data['video']['url']}")

327 print(f"Duration: {data['video']['duration']}s")322 print(f"Duration: {data['video']['duration']}s")

328 break323 break

329 elif data["status"] == "expired":324 elif data["status"] == "failed":

330 print("Request expired")325 print("Video generation failed")

331 break326 break

332 time.sleep(5)327 time.sleep(5)

333```328```

334 329 

335```javascript customLanguage="javascriptAISDK"

336import { xai } from "@ai-sdk/xai";

337import { experimental_generateVideo as generateVideo } from "ai";

338 

339const result = await generateVideo({

340 model: xai.video("grok-imagine-video-1.5"),

341 prompt: "Timelapse of a flower blooming in a sunlit garden",

342 duration: 10,

343 aspectRatio: "16:9",

344 providerOptions: {

345 xai: { resolution: "720p" },

346 },

347});

348 

349const videoUrl = result.providerMetadata?.xai?.videoUrl;

350console.log(videoUrl);

351```

352 

353```bash330```bash

354# Start the video generation request331# Start the video generation request

355REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \332REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \


371 if [ "$STATUS" = "done" ]; then348 if [ "$STATUS" = "done" ]; then

372 echo "$RESULT" | jq -r '.video.url'349 echo "$RESULT" | jq -r '.video.url'

373 break350 break

374 elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then351 elif [ "$STATUS" = "failed" ]; then

375 echo "Request $STATUS"; echo "$RESULT" | jq .352 echo "Request $STATUS"; echo "$RESULT" | jq .

376 break353 break

377 fi354 fi


379done356done

380```357```

381 358 

359```python customLanguage="pythonXAI"

360import os

361import xai_sdk

362 

363client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

364 

365response = client.video.generate(

366 prompt="Timelapse of a flower blooming in a sunlit garden",

367 model="grok-imagine-video-1.5",

368 duration=10,

369 aspect_ratio="16:9",

370 resolution="720p",

371)

372 

373print(f"Video URL: {response.url}")

374print(f"Duration: {response.duration}s")

375```

376 

382### Request Modes377### Request Modes

383 378 

384The video generation endpoint supports multiple modes, determined by which fields are set. Edit and extend use dedicated endpoints; generation modes share `/v1/videos/generations`:379The video generation endpoint supports multiple modes, determined by which fields are set. Edit and extend use dedicated endpoints; generation modes share `/v1/videos/generations`:


406| Python SDK | AI SDK (`providerOptions.xai`) | Description | Default |401| Python SDK | AI SDK (`providerOptions.xai`) | Description | Default |

407|-----------|-------------|-------------|---------|402|-----------|-------------|-------------|---------|

408| `timeout` | `pollTimeoutMs` | Maximum time to wait for the video to complete | 10 minutes |403| `timeout` | `pollTimeoutMs` | Maximum time to wait for the video to complete | 10 minutes |

409| `interval` | `pollIntervalMs` | Time between status checks | 100 milliseconds |404| `interval` | `pollIntervalMs` | Time between status checks | 1 second (Python), 5 seconds (AI SDK) |

410 

411```python customLanguage="pythonXAI"

412import os

413from datetime import timedelta

414import xai_sdk

415 

416client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

417 

418response = client.video.generate(

419 prompt="Epic cinematic drone shot flying through mountain peaks",

420 model="grok-imagine-video-1.5",

421 duration=15,

422 timeout=timedelta(minutes=15), # Wait up to 15 minutes

423 interval=timedelta(seconds=5), # Check every 5 seconds

424)

425 

426print(response.url)

427```

428 405 

429```javascript customLanguage="javascriptAISDK"406```javascript customLanguage="javascriptAISDK"

430import { xai } from "@ai-sdk/xai";407import { xai } from "@ai-sdk/xai";


446console.log(videoUrl);423console.log(videoUrl);

447```424```

448 425 

449If the video isn't ready within the timeout period, the Python SDK raises a `TimeoutError` and the AI SDK aborts via its `AbortSignal`. For even finer control, use the [manual polling approach](#handle-polling-manually); the Python SDK provides `start()` and `get()` methods, while the AI SDK supports a custom `abortSignal` for cancellation.

450 

451## Handle Polling Manually

452 

453For fine-grained control over the generation lifecycle, use `start()` or `extend_start()` to initiate generation/extension requests respectively and `get()` to check status.

454 

455The `get()` method returns a response with a `status` field. Import the status enum from the SDK:

456 

457```python customLanguage="pythonXAI"426```python customLanguage="pythonXAI"

458import os427import os

459import time428from datetime import timedelta

460import xai_sdk429import xai_sdk

461from xai_sdk.proto import deferred_pb2

462 430 

463client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))431client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

464 432 

465# Start the generation request433response = client.video.generate(

466start_response = client.video.start(434 prompt="Epic cinematic drone shot flying through mountain peaks",

467 prompt="A cat lounging in a sunbeam, tail gently swishing",

468 model="grok-imagine-video-1.5",435 model="grok-imagine-video-1.5",

469 duration=5,436 duration=15,

437 timeout=timedelta(minutes=15), # Wait up to 15 minutes

438 interval=timedelta(seconds=5), # Check every 5 seconds

470)439)

471 440 

472print(f"Request ID: {start_response.request_id}")441print(response.url)

442```

473 443 

474# Poll for results444If the video isn't ready within the timeout period, the Python SDK raises a `TimeoutError` and the AI SDK aborts via its `AbortSignal`. For even finer control, use the [manual polling approach](#handle-polling-manually); the Python SDK provides `start()` and `get()` methods, while the AI SDK supports a custom `abortSignal` for cancellation.

475while True:

476 result = client.video.get(start_response.request_id)

477 445 

478 if result.status == deferred_pb2.DeferredStatus.DONE:446## Handle Polling Manually

479 print(f"Video URL: {result.response.video.url}")447 

480 break448For fine-grained control over the generation lifecycle, use `start()` or `extend_start()` to initiate generation/extension requests respectively and `get()` to check status.

481 elif result.status == deferred_pb2.DeferredStatus.EXPIRED:449 

482 print("Request expired")450The `get()` method returns a response with a `status` field. Import the status enum from the SDK:

483 break

484 elif result.status == deferred_pb2.DeferredStatus.FAILED:

485 print("Video generation failed")

486 break

487 elif result.status == deferred_pb2.DeferredStatus.PENDING:

488 print("Still processing...")

489 time.sleep(5)

490```

491 451 

492```python customLanguage="pythonRequests"452```python customLanguage="pythonRequests"

493import os453import os


524 if data["status"] == "done":484 if data["status"] == "done":

525 print(f"Video URL: {data['video']['url']}")485 print(f"Video URL: {data['video']['url']}")

526 break486 break

527 elif data["status"] == "expired":

528 print("Request expired")

529 break

530 elif data["status"] == "failed":487 elif data["status"] == "failed":

531 print("Video generation failed")488 print("Video generation failed")

532 break489 break


564 if (data.status === "done") {521 if (data.status === "done") {

565 console.log(`Video URL: ${data.video.url}`);522 console.log(`Video URL: ${data.video.url}`);

566 break;523 break;

567 } else if (data.status === "expired") {

568 console.log("Request expired");

569 break;

570 } else if (data.status === "failed") {524 } else if (data.status === "failed") {

571 console.log("Video generation failed");525 console.log("Video generation failed");

572 break;526 break;


598 if [ "$STATUS" = "done" ]; then552 if [ "$STATUS" = "done" ]; then

599 echo "$RESULT" | jq -r '.video.url'553 echo "$RESULT" | jq -r '.video.url'

600 break554 break

601 elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then555 elif [ "$STATUS" = "failed" ]; then

602 echo "Request $STATUS"; echo "$RESULT" | jq .556 echo "Request $STATUS"; echo "$RESULT" | jq .

603 break557 break

604 fi558 fi


607done561done

608```562```

609 563 

564```python customLanguage="pythonXAI"

565import os

566import time

567import xai_sdk

568from xai_sdk.proto import deferred_pb2

569 

570client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

571 

572# Start the generation request

573start_response = client.video.start(

574 prompt="A cat lounging in a sunbeam, tail gently swishing",

575 model="grok-imagine-video-1.5",

576 duration=5,

577)

578 

579print(f"Request ID: {start_response.request_id}")

580 

581# Poll for results

582while True:

583 result = client.video.get(start_response.request_id)

584

585 if result.status == deferred_pb2.DeferredStatus.DONE:

586 print(f"Video URL: {result.response.video.url}")

587 break

588 elif result.status == deferred_pb2.DeferredStatus.FAILED:

589 print("Video generation failed")

590 break

591 elif result.status == deferred_pb2.DeferredStatus.PENDING:

592 print("Still processing...")

593 time.sleep(5)

594```

595 

610The available status values are:596The available status values are:

611 597 

612| Proto Value | Description |598| Proto Value | Description |

613|-------------|-------------|599|-------------|-------------|

614| `deferred_pb2.DeferredStatus.PENDING` | Video is still being generated |600| `deferred_pb2.DeferredStatus.PENDING` | Video is still being generated |

615| `deferred_pb2.DeferredStatus.DONE` | Video is ready |601| `deferred_pb2.DeferredStatus.DONE` | Video is ready |

616| `deferred_pb2.DeferredStatus.EXPIRED` | Request has expired |

617| `deferred_pb2.DeferredStatus.FAILED` | Video generation failed |602| `deferred_pb2.DeferredStatus.FAILED` | Video generation failed |

618 603 

619## Error Handling604## Error Handling


696 681 

697The SDK response includes the generated video and provider-specific metadata. In the AI SDK, the SpaceXAI-hosted output URL is available at `providerMetadata.xai.videoUrl`.682The SDK response includes the generated video and provider-specific metadata. In the AI SDK, the SpaceXAI-hosted output URL is available at `providerMetadata.xai.videoUrl`.

698 683 

699```python customLanguage="pythonXAI"

700if response.respect_moderation:

701 print(response.url)

702else:

703 print("Video filtered by moderation")

704 

705print(f"Duration: {response.duration} seconds")

706print(f"Model: {response.model}")

707```

708 

709```javascript customLanguage="javascriptAISDK"684```javascript customLanguage="javascriptAISDK"

710const result = await generateVideo({685const result = await generateVideo({

711 model: xai.video("grok-imagine-video-1.5"),686 model: xai.video("grok-imagine-video-1.5"),


716console.log(result.providerMetadata?.xai?.videoUrl);691console.log(result.providerMetadata?.xai?.videoUrl);

717```692```

718 693 

694```python customLanguage="pythonXAI"

695if response.respect_moderation:

696 print(response.url)

697else:

698 print("Video filtered by moderation")

699 

700print(f"Duration: {response.duration} seconds")

701print(f"Model: {response.model}")

702```

703 

719## Concurrent Requests704## Concurrent Requests

720 705 

721When you need to generate multiple videos, run requests concurrently. This is especially useful for comparing prompts or creating multiple variations.706When you need to generate multiple videos, run requests concurrently. This is especially useful for comparing prompts or creating multiple variations.

Details

1#### Model Capabilities

2 

3# Video Overview

4 

5Grok Imagine video models turn a set of references, a still image, an existing clip, or a prompt into video with generated audio. `grok-imagine-video-1.5` generates lip-synced speech, renders text-to-video and image-to-video at native 1080p, takes up to 14 reference images and 3 voice references, and pins first, last, and mid-video frames, in clips up to 15 seconds. `grok-imagine-video-1.5-lite` does text-to-video and image-to-video with lip-synced speech at the lowest price per second, reaching 1080p by upscaling 720p. We recommend `grok-imagine-video-1.5-lite` for simple videos, and `grok-imagine-video-1.5` when you need references, voices, pinned frames, or native 1080p.

6 

7Requests are asynchronous: submit, poll with the returned `request_id`, then download the clip. The xAI SDK and the Vercel AI SDK poll for you ([how it works](/developers/model-capabilities/video/generation#how-it-works)).

8 

9## Capabilities and specifications

10 

11| Capability | `grok-imagine-video-1.5` | `grok-imagine-video-1.5-lite` | `grok-imagine-video` |

12| --- | --- | --- | --- |

13| [Text-to-video](/developers/model-capabilities/video/generation) | Yes: Up to 1080p, native | Yes: Up to 1080p, upscaled from 720p | Yes: Up to 720p |

14| [Image-to-video](/developers/model-capabilities/video/image-to-video) | Yes: Up to 1080p, native | Yes: Up to 1080p, upscaled from 720p | Yes: Up to 720p |

15| [Reference images](/developers/model-capabilities/video/reference-to-video) | Yes: Up to 14 images, 15 s, 720p | Not supported | Yes: Up to 7 images, 10 s, 720p |

16| [Voice references](/developers/model-capabilities/video/reference-to-video#reference-audio) | Yes: Up to 3 voices | Not supported | Not supported |

17| [First & last frame](/developers/model-capabilities/video/reference-to-video#first--last-frame) | Yes | Not supported | Not supported |

18| [Keyframes](/developers/model-capabilities/video/reference-to-video#keyframes) | Yes: Up to 4 mid-video frames | Not supported | Not supported |

19| [Video editing](/developers/model-capabilities/video/editing) | Not supported | Not supported | Yes: Source up to 8.7 s |

20| [Video extension](/developers/model-capabilities/video/extension) | Not supported | Not supported | Yes: Adds 2–10 s |

21| Duration | Yes: 1–15 s | Yes: 1–15 s | Yes: 1–15 s |

22| [Resolution](/developers/model-capabilities/video/generation#resolution) | Yes: 480p, 720p, 1080p | Yes: 480p, 720p, 1080p | Yes: 480p, 720p |

23| [Aspect ratio](/developers/model-capabilities/video/generation#aspect-ratio) | Yes: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9, 5:2 | Yes: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9, 5:2 | Yes: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9, 5:2 |

24| [Image-to-video aspect ratio](/developers/model-capabilities/video/generation#aspect-ratio) | Yes: Automatic: matches the input image | Yes: Automatic: matches the input image | Yes: Automatic: matches the input image |

25| [Audio](/developers/model-capabilities/video/generation#audio) | Yes: Native audio with lip-synced speech | Yes: Native audio with lip-synced speech | Yes: Generated audio |

26 

27Per-second prices by resolution are listed on the [Models](/developers/models) page.

28 

29## Reference-to-video

30 

31Reference images supply the characters, products, and locations without fixing the first frame: up to 14 per request on `grok-imagine-video-1.5`, 7 on `grok-imagine-video`. On `grok-imagine-video-1.5`, `reference_audios` gives up to three characters a preset voice, with lip-synced speech. Voice references from your own audio files are available to trusted partners [on request](https://x.ai/contact-sales?interest=imagine).

32 

33## First, key, and last frames

34 

35On `grok-imagine-video-1.5`, `image` sets the first frame, `last_frame` the last, and up to four `keyframes` pin frames at chosen timestamps; the model fills in the motion. Pass the same image as `image` and `last_frame` to make a loop.

36 

37## Image-to-video

38 

39The still you pass as `image` becomes the first frame, and the prompt describes what happens next. The output always matches the still's aspect ratio; `aspect_ratio` is ignored.

40 

41## Text-to-video

42 

43Describe the shot, including camera movement, lighting, and sound. On `grok-imagine-video-1.5` and `grok-imagine-video-1.5-lite`, the model renders a first frame from the prompt, then animates it.

44 

45## Video editing

46 

47Send a clip and an instruction to `grok-imagine-video`. It applies the change and keeps everything else as close to the source as it can; say what must stay the same, as these prompts do.

48 

49## Related

50 

51* [Video Generation](/developers/model-capabilities/video/generation): Configuration, polling, and error handling

52* [Image-to-Video](/developers/model-capabilities/video/image-to-video): Animate a still image

53* [Reference-to-Video](/developers/model-capabilities/video/reference-to-video): References, voices, and pinned frames

54* [Video Editing](/developers/model-capabilities/video/editing) and [Video Extension](/developers/model-capabilities/video/extension): Change or continue an existing clip

55* [Models](/developers/models): Pricing and rate limits for every video model

Details

2 2 

3# Reference-to-Video3# Reference-to-Video

4 4 

5Provide reference images, a preset voice, or both to guide the generated video. Images incorporate specific people, objects, clothing, or other visual elements without locking the first frame (unlike [image-to-video](/developers/model-capabilities/video/image-to-video)). This is useful for virtual try-on, product placement, character-consistent storytelling, and voice identity. On `grok-imagine-video-1.5`, you can also pick the voice your subject speaks in (see [Reference audio](#reference-audio)), pin the exact first or last frame (see [First & Last frame](#first--last-frame)), and pin frames at chosen moments inside the clip (see [Keyframes](#keyframes)).5Provide reference images, a preset voice, or both to guide the generated video. Images incorporate specific people, objects, clothing, or other visual elements without locking the first frame (unlike [image-to-video](/developers/model-capabilities/video/image-to-video)). This is useful for virtual try-on, product placement, and stories with recurring characters. On `grok-imagine-video-1.5`, you can also pick the voice your subject speaks in (see [Reference audio](#reference-audio)), pin the exact first or last frame (see [First & Last frame](#first--last-frame)), and pin frames at chosen moments inside the clip (see [Keyframes](#keyframes)).

6 6 

7Each reference image can be provided as a public HTTPS URL, a base64-encoded data URI, or a `file_id` from the [Files API](/developers/files) — and you can mix kinds within a single request. See [Imagine → Files API Integration](/developers/model-capabilities/imagine/files/inputs) for `file_id` details and examples.7Each reference image can be provided as a public HTTPS URL, a base64-encoded data URI, or a `file_id` from the [Files API](/developers/files) — and you can mix kinds within a single request. See [Imagine → Files API Integration](/developers/model-capabilities/imagine/files/inputs) for `file_id` details and examples.

8 8 

9In the Vercel AI SDK, set `providerOptions.xai.mode` to `"reference-to-video"` and pass the images with `providerOptions.xai.referenceImageUrls`.9In the Vercel AI SDK, set `providerOptions.xai.mode` to `"reference-to-video"` and pass the images with `providerOptions.xai.referenceImageUrls`.

10 10 

11> [!WARNING]11Prompts are capped at 4,096 bytes. With many references, give each one a single role and a fixed place in the scene, keep the cast small, and keep props static.

12 12 

13Refer to reference images in the prompt by their position in the list, starting at `<IMAGE_0>` for the first image, then `<IMAGE_1>`, and so on. If you also set `image` to pin the first frame, it takes `<IMAGE_0>` and your reference images start at `<IMAGE_1>`.13| Limit | Value |

14|-------|-------|

15| Reference images | Up to **14** per request on `grok-imagine-video-1.5`; up to **7** on `grok-imagine-video` |

16| Voice references | Up to **3** per request on `grok-imagine-video-1.5`, preset voices selected by `voice_id` (see [Reference audio](#reference-audio)) |

17| Pinned frames | `grok-imagine-video-1.5` only: `image` as the first frame alongside references, `last_frame`, and up to **4** `keyframes` (see [First & Last frame](#first--last-frame) and [Keyframes](#keyframes)) |

18| Required input | At least one reference image, voice, `last_frame`, or `keyframes` entry |

19| Duration | Up to **15 seconds** on `grok-imagine-video-1.5`; up to **10 seconds** on `grok-imagine-video` |

20| Resolution | Up to **720p** |

14 21 

15```python customLanguage="pythonXAI"22Refer to reference images in the prompt by their position in the list, starting at `<IMAGE_0>` for the first image, then `<IMAGE_1>`, and so on. If you also set `image` to pin the first frame, it takes `<IMAGE_0>` and your reference images start at `<IMAGE_1>`.

16import os

17import xai_sdk

18 23 

19client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))24```javascript customLanguage="javascriptAISDK"

25import { xai } from "@ai-sdk/xai";

26import { experimental_generateVideo as generateVideo } from "ai";

20 27 

21response = client.video.generate(28const result = await generateVideo({

22 prompt="slow zoom in on the white fashion runway stage. then, the model from <IMAGE_0> walks in from the back of the shot from the white opening, and gracefully walk out onto the front of the white stage platform. they wear the shirt from <IMAGE_1> and black flared jeans. they look dramatically at the camera. high quality slow motion shot. fun, playful. skin pores. highly detailed faces. perfect shot. they reach the end of the runway and look at the camera as the camera slowly zooms. subtle smile.",29 model: xai.video("grok-imagine-video-1.5"),

23 model="grok-imagine-video-1.5",30 prompt: "slow zoom in on the white fashion runway stage. then, the model from <IMAGE_0> walks in from the back of the shot from the white opening, and gracefully walk out onto the front of the white stage platform. they wear the shirt from <IMAGE_1> and black flared jeans. they look dramatically at the camera. high quality slow motion shot. fun, playful. skin pores. highly detailed faces. perfect shot. they reach the end of the runway and look at the camera as the camera slowly zooms. subtle smile.",

24 reference_image_urls=[31 duration: 10,

32 aspectRatio: "16:9",

33 providerOptions: {

34 xai: {

35 mode: "reference-to-video",

36 referenceImageUrls: [

25 "<IMAGE_URL_1>",37 "<IMAGE_URL_1>",

26 "<IMAGE_URL_2>",38 "<IMAGE_URL_2>",

27 "<IMAGE_URL_3>",39 "<IMAGE_URL_3>",

28 ],40 ],

29 duration=10,41 resolution: "720p",

30 aspect_ratio="16:9",42 pollTimeoutMs: 600000,

31 resolution="720p",43 },

32)44 },

45});

33 46 

34print(response.url)47const videoUrl = result.providerMetadata?.xai?.videoUrl;

48console.log(videoUrl);

35```49```

36 50 

37```python customLanguage="pythonRequests"51```python customLanguage="pythonRequests"


72 if data["status"] == "done":86 if data["status"] == "done":

73 print(data["video"]["url"])87 print(data["video"]["url"])

74 break88 break

75 elif data["status"] == "expired":89 elif data["status"] == "failed":

76 print("Request expired")90 print("Video generation failed")

77 break91 break

78 time.sleep(5)92 time.sleep(5)

79```93```

80 94 

81```javascript customLanguage="javascriptAISDK"

82import { xai } from "@ai-sdk/xai";

83import { experimental_generateVideo as generateVideo } from "ai";

84 

85const result = await generateVideo({

86 model: xai.video("grok-imagine-video-1.5"),

87 prompt: "slow zoom in on the white fashion runway stage. then, the model from <IMAGE_0> walks in from the back of the shot from the white opening, and gracefully walk out onto the front of the white stage platform. they wear the shirt from <IMAGE_1> and black flared jeans. they look dramatically at the camera. high quality slow motion shot. fun, playful. skin pores. highly detailed faces. perfect shot. they reach the end of the runway and look at the camera as the camera slowly zooms. subtle smile.",

88 duration: 10,

89 aspectRatio: "16:9",

90 providerOptions: {

91 xai: {

92 mode: "reference-to-video",

93 referenceImageUrls: [

94 "<IMAGE_URL_1>",

95 "<IMAGE_URL_2>",

96 "<IMAGE_URL_3>",

97 ],

98 resolution: "720p",

99 pollTimeoutMs: 600000,

100 },

101 },

102});

103 

104const videoUrl = result.providerMetadata?.xai?.videoUrl;

105console.log(videoUrl);

106```

107 

108```bash95```bash

109# Start the reference-to-video request96# Start the reference-to-video request

110REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \97REQUEST_ID=$(curl -s -X POST https://api.x.ai/v1/videos/generations \


131 if [ "$STATUS" = "done" ]; then118 if [ "$STATUS" = "done" ]; then

132 echo "$RESULT" | jq -r '.video.url'119 echo "$RESULT" | jq -r '.video.url'

133 break120 break

134 elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then121 elif [ "$STATUS" = "failed" ]; then

135 echo "Request $STATUS"; echo "$RESULT" | jq .122 echo "Request $STATUS"; echo "$RESULT" | jq .

136 break123 break

137 fi124 fi


139done126done

140```127```

141 128 

142## Reference audio

143 

144> [!WARNING]

145>

146> Preset voices are generally available. Voice references with your own audio files are available to trusted partners, on request. .

147 

148On `grok-imagine-video-1.5`, give your subject a voice by passing up to **3** preset voices with `reference_audios`. Each entry names a voice by `voice_id`, drawn from the same built-in roster as [Text to Speech](/developers/model-capabilities/audio/text-to-speech#voices), so `{"voice_id": "eve"}` speaks in Eve's voice. Identifiers are case-insensitive; an unknown one returns `400` with the list of available voices. You can hear every voice in the [flagship voices announcement](https://x.ai/news/new-flagship-voices).

149 

150`reference_audios` accepts preset voices; voice references with your own audio files are available to trusted partners [on request](https://x.ai/contact-sales?interest=imagine). Use a voice alongside reference images or on its own, and tag voices in the prompt as `<AUDIO_0>`, `<AUDIO_1>`, and `<AUDIO_2>` (with `<IMAGE_0>`… when you also pass images).

151 

152```python customLanguage="pythonXAI"129```python customLanguage="pythonXAI"

153import os130import os

154import xai_sdk131import xai_sdk


156client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))133client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

157 134 

158response = client.video.generate(135response = client.video.generate(

159 prompt="The person from <IMAGE_0> presents the product from <IMAGE_1> on the set from <IMAGE_2>, speaking with the voice from <AUDIO_0>. A second speaker with the voice from <AUDIO_1> replies.",136 prompt="slow zoom in on the white fashion runway stage. then, the model from <IMAGE_0> walks in from the back of the shot from the white opening, and gracefully walk out onto the front of the white stage platform. they wear the shirt from <IMAGE_1> and black flared jeans. they look dramatically at the camera. high quality slow motion shot. fun, playful. skin pores. highly detailed faces. perfect shot. they reach the end of the runway and look at the camera as the camera slowly zooms. subtle smile.",

160 model="grok-imagine-video-1.5",137 model="grok-imagine-video-1.5",

161 reference_image_urls=[138 reference_image_urls=[

162 "<IMAGE_URL_1>",139 "<IMAGE_URL_1>",

163 "<IMAGE_URL_2>",140 "<IMAGE_URL_2>",

164 "<IMAGE_URL_3>",141 "<IMAGE_URL_3>",

165 ],142 ],

166 reference_audios=[143 duration=10,

167 {"voice_id": "eve"},144 aspect_ratio="16:9",

168 {"voice_id": "leo"},

169 ],

170 duration=8,

171 aspect_ratio="9:16",

172 resolution="720p",145 resolution="720p",

173)146)

174 147 

175print(response.url)148print(response.url)

176```149```

177 150 

151## Reference audio

152 

153On `grok-imagine-video-1.5`, give your subject a voice by passing up to **3** preset voices with `reference_audios`. Each entry names a voice by `voice_id`, drawn from the same built-in roster as [Text to Speech](/developers/model-capabilities/audio/text-to-speech#voices), so `{"voice_id": "eve"}` speaks in Eve's voice. Identifiers are case-insensitive; an unknown one returns `400` with the list of available voices. You can hear every voice in the [flagship voices announcement](https://x.ai/news/new-flagship-voices).

154 

155`reference_audios` accepts preset voices; voice references with your own audio files are available to trusted partners [on request](https://x.ai/contact-sales?interest=imagine). Use a voice alongside reference images or on its own, and tag voices in the prompt as `<AUDIO_0>`, `<AUDIO_1>`, and `<AUDIO_2>` (with `<IMAGE_0>`… when you also pass images).

156 

178```python customLanguage="pythonRequests"157```python customLanguage="pythonRequests"

179import os158import os

180import time159import time


217 if data["status"] == "done":196 if data["status"] == "done":

218 print(data["video"]["url"])197 print(data["video"]["url"])

219 break198 break

220 elif data["status"] == "expired":199 elif data["status"] == "failed":

221 print("Request expired")200 print("Video generation failed")

222 break201 break

223 time.sleep(5)202 time.sleep(5)

224```203```


251 if [ "$STATUS" = "done" ]; then230 if [ "$STATUS" = "done" ]; then

252 echo "$RESULT" | jq -r '.video.url'231 echo "$RESULT" | jq -r '.video.url'

253 break232 break

254 elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then233 elif [ "$STATUS" = "failed" ]; then

255 echo "Request $STATUS"; echo "$RESULT" | jq .234 echo "Request $STATUS"; echo "$RESULT" | jq .

256 break235 break

257 fi236 fi


259done238done

260```239```

261 240 

262## First & Last frame

263 

264On `grok-imagine-video-1.5`, `last_frame` pins the exact last frame of the clip. The video ends arriving on that image rather than re-rendering it as a reference. `image` combined with `reference_images`, `reference_audios`, or `last_frame` is the matching first-frame pin.

265 

266| Request shape | Result |

267|---------------|--------|

268| `image` + `last_frame` | Pinned first and last frame. The model interpolates between the two. |

269| `last_frame` only | Pinned last frame. The model generates the opening and lands on the pinned image. |

270| `last_frame` + `reference_images` / `reference_audios` | Pinned last frame with reference guidance. Add `image` to pin the first frame as well. |

271 

272`prompt` is optional in every first & last frame request. Include one to steer motion and camera work between the frames; omit it to let the frames alone drive the clip.

273 

274`last_frame` uses the same URL, data-URI, and `file_id` shapes as [image-to-video](/developers/model-capabilities/video/image-to-video). In the xAI Python SDK, pass `last_frame_url` (or `last_frame_file_id` for a Files API upload) alongside `image_url` (or `image_file_id`). The Vercel AI SDK does not expose `last_frame` yet; send it on the REST body.

275 

276Classic `grok-imagine-video` rejects `last_frame` and rejects combining `image` with reference inputs.

277 

278```python customLanguage="pythonXAI"241```python customLanguage="pythonXAI"

279import os242import os

280import xai_sdk243import xai_sdk


282client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))245client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

283 246 

284response = client.video.generate(247response = client.video.generate(

285 prompt="The camera dollies from the sunlit doorway to the window, settling on the closing frame.",248 prompt="The person from <IMAGE_0> presents the product from <IMAGE_1> on the set from <IMAGE_2>, speaking with the voice from <AUDIO_0>. A second speaker with the voice from <AUDIO_1> replies.",

286 model="grok-imagine-video-1.5",249 model="grok-imagine-video-1.5",

287 image_url="<FIRST_FRAME_URL>",250 reference_image_urls=[

288 last_frame_url="<LAST_FRAME_URL>",251 "<IMAGE_URL_1>",

252 "<IMAGE_URL_2>",

253 "<IMAGE_URL_3>",

254 ],

255 reference_audios=[

256 {"voice_id": "eve"},

257 {"voice_id": "leo"},

258 ],

289 duration=8,259 duration=8,

290 aspect_ratio="16:9",260 aspect_ratio="9:16",

291 resolution="720p",261 resolution="720p",

292)262)

293 263 

294print(response.url)264print(response.url)

295```265```

296 266 

267## First & Last frame

268 

269On `grok-imagine-video-1.5`, `last_frame` pins the exact last frame of the clip. The video ends arriving on that image rather than re-rendering it as a reference. `image` combined with `reference_images`, `reference_audios`, or `last_frame` is the matching first-frame pin.

270 

271| Request shape | Result |

272|---------------|--------|

273| `image` + `last_frame` | Pinned first and last frame. The model interpolates between the two. |

274| `last_frame` only | Pinned last frame. The model generates the opening and lands on the pinned image. |

275| `last_frame` + `reference_images` / `reference_audios` | Pinned last frame with reference guidance. Add `image` to pin the first frame as well. |

276 

277`prompt` is optional in every first & last frame request. Include one to steer motion and camera work between the frames; omit it to let the frames alone drive the clip.

278 

279`last_frame` uses the same URL, data-URI, and `file_id` shapes as [image-to-video](/developers/model-capabilities/video/image-to-video). In the xAI Python SDK, pass `last_frame_url` (or `last_frame_file_id` for a Files API upload) alongside `image_url` (or `image_file_id`). The Vercel AI SDK does not expose `last_frame` yet; send it on the REST body.

280 

297```python customLanguage="pythonRequests"281```python customLanguage="pythonRequests"

298import os282import os

299import time283import time


329 if data["status"] == "done":313 if data["status"] == "done":

330 print(data["video"]["url"])314 print(data["video"]["url"])

331 break315 break

332 elif data["status"] == "expired":316 elif data["status"] == "failed":

333 print("Request expired")317 print("Video generation failed")

334 break318 break

335 time.sleep(5)319 time.sleep(5)

336```320```


356 if [ "$STATUS" = "done" ]; then340 if [ "$STATUS" = "done" ]; then

357 echo "$RESULT" | jq -r '.video.url'341 echo "$RESULT" | jq -r '.video.url'

358 break342 break

359 elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "expired" ]; then343 elif [ "$STATUS" = "failed" ]; then

360 echo "Request $STATUS"; echo "$RESULT" | jq .344 echo "Request $STATUS"; echo "$RESULT" | jq .

361 break345 break

362 fi346 fi


364done348done

365```349```

366 350 

351```python customLanguage="pythonXAI"

352import os

353import xai_sdk

354 

355client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

356 

357response = client.video.generate(

358 prompt="The camera dollies from the sunlit doorway to the window, settling on the closing frame.",

359 model="grok-imagine-video-1.5",

360 image_url="<FIRST_FRAME_URL>",

361 last_frame_url="<LAST_FRAME_URL>",

362 duration=8,

363 aspect_ratio="16:9",

364 resolution="720p",

365)

366 

367print(response.url)

368```

369 

367## Keyframes370## Keyframes

368 371 

369On `grok-imagine-video-1.5`, `keyframes` pins images at chosen moments inside the clip. Each entry pairs an `image` with a `timestamp_s`, and the video passes through that exact image at that time. Use it to storyboard a shot: the model generates the motion between your anchors rather than inventing the whole clip from one frame.372On `grok-imagine-video-1.5`, `keyframes` pins images at chosen moments inside the clip. Each entry pairs an `image` with a `timestamp_s`, and the video passes through that exact image at that time. Use it to storyboard a shot: the model generates the motion between your anchors rather than inventing the whole clip from one frame.


380| Constraint | Detail |383| Constraint | Detail |

381|------------|--------|384|------------|--------|

382| Count | At most 4 keyframes per request. |385| Count | At most 4 keyframes per request. |

383| Timing | `timestamp_s` must fall strictly inside the clip: greater than 0 and less than `duration`. Use `image` / `last_frame` for the endpoints. |386| Timing | `timestamp_s` must fall strictly inside the clip: greater than 0 and less than `duration`. |

384| Spacing | Anchors snap to a 1/3-second grid. Two keyframes that round to the same slot are rejected, so keep them at least 1/3 s apart. |387| Spacing | Anchors snap to a 1/3-second grid. Two keyframes that round to the same slot are rejected, so keep them at least 1/3 s apart. |

385| Inputs | Each `image` accepts the same URL, data-URI, and `file_id` shapes as [image-to-video](/developers/model-capabilities/video/image-to-video). |388| Inputs | Each `image` accepts the same URL, data-URI, and `file_id` shapes as [image-to-video](/developers/model-capabilities/video/image-to-video). |

386 389 

387In the xAI Python SDK, `keyframes` is a list of dicts that flatten the REST `image` object: each entry takes `image_url` (or `image_file_id`) and `timestamp`, the REST `timestamp_s` in seconds, as in `{"image_url": "<KEYFRAME_URL_1>", "timestamp": 2.0}`. Pin the endpoints with `image_url` and `last_frame_url`. The Vercel AI SDK does not expose `keyframes` yet; send it on the REST body.390In the xAI Python SDK, `keyframes` is a list of dicts that flatten the REST `image` object: each entry takes `image_url` (or `image_file_id`) and `timestamp`, the REST `timestamp_s` in seconds, as in `{"image_url": "<KEYFRAME_URL_1>", "timestamp": 2.0}`. Pin the endpoints with `image_url` and `last_frame_url`. The Vercel AI SDK does not expose `keyframes` yet; send it on the REST body.

388 391 

389Classic `grok-imagine-video` rejects `keyframes`, and keyframes cannot be combined with video editing.392### Example: a year that loops

390 

391### Example: a year in one request

392 

393This request uses every kind of pin at once. A single oak is pinned as winter in the first frame, spring and summer as keyframes at 3 and 6 seconds, and autumn as the last frame at 9 seconds; the model animates the seasons in between. Select a pin to see the exact frame the video passes through, or press play to watch the whole year.

394 393 

395Pins work best when they share a scene. The four stills started as one generated winter image, and each season is an [image edit](/developers/model-capabilities/images/editing) of it, so the tree, horizon, and camera angle stay identical and the model only has to animate the change of season.394Pins work best when they share a scene: generate one still, then make the others with [image editing](/developers/model-capabilities/images/editing) so only what should change differs. To loop a clip, pass the same image as `image` and `last_frame`.

396 395 

397## Related396## Related

398 397 

quickstart.md +57 −57

Details

26 26 

27Pick your language and install the SDK:27Pick your language and install the SDK:

28 28 

29```bash customLanguage="pythonXAI"29```bash customLanguage="javascriptAISDK"

30pip install xai-sdk30npm install ai @ai-sdk/xai zod

31```31```

32 32 

33```bash customLanguage="pythonOpenAISDK"33```bash customLanguage="pythonOpenAISDK"

34pip install openai34pip install openai

35```35```

36 36 

37```bash customLanguage="javascriptAISDK"

38npm install ai @ai-sdk/xai zod

39```

40 

41```bash customLanguage="javascriptOpenAISDK"37```bash customLanguage="javascriptOpenAISDK"

42npm install openai38npm install openai

43```39```

44 40 

41```bash customLanguage="pythonXAI"

42pip install xai-sdk

43```

44 

45## Step 4: Make your first request45## Step 4: Make your first request

46 46 

47Send a coding prompt to [Grok Build](/build/overview) (`grok-4.7`) and get a response. The same model powers agentic coding in Grok Build and is available on the SpaceXAI API:47Send a coding prompt to [Grok Build](/build/overview) (`grok-4.7`) and get a response. The same model powers agentic coding in Grok Build and is available on the SpaceXAI API:

48 48 

49```bash49```javascript customLanguage="javascriptAISDK"

50curl https://api.x.ai/v1/responses \50import { xai } from '@ai-sdk/xai';

51 -H "Authorization: Bearer $XAI_API_KEY" \51import { generateText } from 'ai';

52 -H "Content-Type: application/json" \

53 -d '{

54 "model": "grok-4.7",

55 "input": "Fix this function and explain the bug: function median(a){a.sort();return a[a.length/2]}"

56 }'

57```

58 

59```python customLanguage="pythonXAI"

60import os

61from xai_sdk import Client

62from xai_sdk.chat import user

63 

64client = Client(api_key=os.getenv("XAI_API_KEY"))

65 52 

66chat = client.chat.create(model="grok-4.7")53const { text } = await generateText({

67chat.append(user("Fix this function and explain the bug: function median(a){a.sort();return a[a.length/2]}"))54 model: xai.responses('grok-4.7'),

55 prompt: 'Fix this function and explain the bug: function median(a){a.sort();return a[a.length/2]}',

56});

68 57 

69print(chat.sample().content)58console.log(text);

70```59```

71 60 

72```python customLanguage="pythonOpenAISDK"61```python customLanguage="pythonOpenAISDK"


85print(response.output_text)74print(response.output_text)

86```75```

87 76 

88```javascript customLanguage="javascriptAISDK"77```bash

89import { xai } from '@ai-sdk/xai';78curl https://api.x.ai/v1/responses \

90import { generateText } from 'ai';79 -H "Authorization: Bearer $XAI_API_KEY" \

91 80 -H "Content-Type: application/json" \

92const { text } = await generateText({81 -d '{

93 model: xai.responses('grok-4.7'),82 "model": "grok-4.7",

94 prompt: 'Fix this function and explain the bug: function median(a){a.sort();return a[a.length/2]}',83 "input": "Fix this function and explain the bug: function median(a){a.sort();return a[a.length/2]}"

95});84 }'

96 

97console.log(text);

98```85```

99 86 

100```javascript customLanguage="javascriptOpenAISDK"87```javascript customLanguage="javascriptOpenAISDK"


113console.log(response.output_text);100console.log(response.output_text);

114```101```

115 102 

103```python customLanguage="pythonXAI"

104import os

105from xai_sdk import Client

106from xai_sdk.chat import user

107 

108client = Client(api_key=os.getenv("XAI_API_KEY"))

109 

110chat = client.chat.create(model="grok-4.7")

111chat.append(user("Fix this function and explain the bug: function median(a){a.sort();return a[a.length/2]}"))

112 

113print(chat.sample().content)

114```

115 

116For multi-turn chat, reasoning, and [structured outputs](/developers/model-capabilities/text/structured-outputs), see the [Text Generation Guide](/developers/model-capabilities/text/generate-text). For agentic coding workflows, see the [Grok Build overview](/build/overview). For AI teammates on a cloud computer, see [Grok Bot](/grok-bot/overview).116For multi-turn chat, reasoning, and [structured outputs](/developers/model-capabilities/text/structured-outputs), see the [Text Generation Guide](/developers/model-capabilities/text/generate-text). For agentic coding workflows, see the [Grok Build overview](/build/overview). For AI teammates on a cloud computer, see [Grok Bot](/grok-bot/overview).

117 117 

118## Step 5: Generate an image118## Step 5: Generate an image

119 119 

120Use the Imagine API to generate images from text prompts:120Use the Imagine API to generate images from text prompts:

121 121 

122```python customLanguage="pythonXAI"122```javascript customLanguage="javascriptAISDK"

123import os123import { xai } from '@ai-sdk/xai';

124import xai_sdk124import { generateImage } from 'ai';

125 

126client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

127 125 

128response = client.image.sample(126const { image } = await generateImage({

129 prompt="A futuristic city skyline at sunset",127 model: xai.image('grok-imagine-image-2.0'),

130 model="grok-imagine-image-2.0",128 prompt: 'A futuristic city skyline at sunset',

131)129});

132 130 

133print(response.url)131console.log(image.base64);

134```132```

135 133 

136```python customLanguage="pythonOpenAISDK"134```python customLanguage="pythonOpenAISDK"


150print(response.data[0].url)148print(response.data[0].url)

151```149```

152 150 

153```javascript customLanguage="javascriptAISDK"

154import { xai } from '@ai-sdk/xai';

155import { generateImage } from 'ai';

156 

157const { image } = await generateImage({

158 model: xai.image('grok-imagine-image-2.0'),

159 prompt: 'A futuristic city skyline at sunset',

160});

161 

162console.log(image.base64);

163```

164 

165```javascript customLanguage="javascriptOpenAISDK"151```javascript customLanguage="javascriptOpenAISDK"

166import OpenAI from 'openai';152import OpenAI from 'openai';

167 153 


188 }'174 }'

189```175```

190 176 

177```python customLanguage="pythonXAI"

178import os

179import xai_sdk

180 

181client = xai_sdk.Client(api_key=os.getenv("XAI_API_KEY"))

182 

183response = client.image.sample(

184 prompt="A futuristic city skyline at sunset",

185 model="grok-imagine-image-2.0",

186)

187 

188print(response.url)

189```

190 

191For more advanced use cases like batch generation, aspect ratio control, and image editing, check out the [Image Generation Guide](/developers/model-capabilities/images/generation).191For more advanced use cases like batch generation, aspect ratio control, and image editing, check out the [Image Generation Guide](/developers/model-capabilities/images/generation).

192 192 

193## What's next193## What's next

rate-limits.md +2 −2

Details

42| grok-build-0.1 | T0: 37, T1: 50, T2: 75, T3: 125, T4: 208 | T0: 10M, T1: 15M, T2: 25M, T3: 45M, T4: 85M |42| grok-build-0.1 | T0: 37, T1: 50, T2: 75, T3: 125, T4: 208 | T0: 10M, T1: 15M, T2: 25M, T3: 45M, T4: 85M |

43| grok-4.20-multi-agent-0309 | T0: 9, T1: 12, T2: 18, T3: 31, T4: 56 | T0: 2.5M, T1: 3.7M, T2: 6.2M, T3: 11M, T4: 21M |43| grok-4.20-multi-agent-0309 | T0: 9, T1: 12, T2: 18, T3: 31, T4: 56 | T0: 2.5M, T1: 3.7M, T2: 6.2M, T3: 11M, T4: 21M |

44| grok-imagine-image-2.0 | T0: 6, T1: 12, T2: 25, T3: 50, T4: 100 | — |44| grok-imagine-image-2.0 | T0: 6, T1: 12, T2: 25, T3: 50, T4: 100 | — |

45| grok-imagine-image | T0: 6, T1: 12, T2: 25, T3: 50, T4: 100 | — |

46| grok-imagine-image-quality | T0: 6, T1: 12, T2: 25, T3: 50, T4: 100 | — |45| grok-imagine-image-quality | T0: 6, T1: 12, T2: 25, T3: 50, T4: 100 | — |

46| grok-imagine-image | T0: 6, T1: 12, T2: 25, T3: 50, T4: 100 | — |

47| grok-imagine-video | T0: 10, T1: 20, T2: 39, T3: 79, T4: 158 | — |47| grok-imagine-video | T0: 10, T1: 20, T2: 39, T3: 79, T4: 158 | — |

48| grok-imagine-video-1.5 | T0: 10, T1: 20, T2: 39, T3: 79, T4: 158 | — |

49| grok-imagine-video-1.5-lite | T0: 10, T1: 20, T2: 39, T3: 79, T4: 158 | — |48| grok-imagine-video-1.5-lite | T0: 10, T1: 20, T2: 39, T3: 79, T4: 158 | — |

49| grok-imagine-video-1.5 | T0: 10, T1: 20, T2: 39, T3: 79, T4: 158 | — |

50 50 

51**Voice & Audio**51**Voice & Audio**

52 52 

Details

33* [Collections API](/developers/rest-api-reference/collections)33* [Collections API](/developers/rest-api-reference/collections)

34* [Management API](/developers/rest-api-reference/management)34* [Management API](/developers/rest-api-reference/management)

35 35 

36## Other protocols

37 

38* [gRPC API](/developers/grpc-api-reference)

39 

40For status codes and their likely causes, see [Debugging Errors](/developers/debugging).36For status codes and their likely causes, see [Debugging Errors](/developers/debugging).

Details

112 112 

113### Code Examples113### Code Examples

114 114 

115```bash

116curl -s https://api.x.ai/v1/images/generations \

117 -H "Content-Type: application/json" \

118 -H "Authorization: Bearer $XAI_API_KEY" \

119 -d '{

120 "model": "grok-imagine-image-2.0",

121 "prompt": "A collage of London landmarks in a stenciled street‑art style"

122 }'

123```

124 

125```pythonOpenAISDK115```pythonOpenAISDK

126import os116import os

127 117 


140print(response.model_dump_json(indent=2))130print(response.model_dump_json(indent=2))

141```131```

142 132 

133```bash

134curl -s https://api.x.ai/v1/images/generations \

135 -H "Content-Type: application/json" \

136 -H "Authorization: Bearer $XAI_API_KEY" \

137 -d '{

138 "model": "grok-imagine-image-2.0",

139 "prompt": "A collage of London landmarks in a stenciled street‑art style"

140 }'

141```

142 

143```javascriptOpenAISDK143```javascriptOpenAISDK

144import OpenAI from "openai";144import OpenAI from "openai";

145 145 


373 ]373 ]

374}374}

375```375```

376 

377***

378 

379## GET /v1/images/\{request\_id}

380 

381\`GET /v1/images/\{request\_id}\`: 202 while pending, 200 with \`data\` when

382done or with \`error\` when failed (a failed generation is a body, not an

383HTTP error, so pollers do not retry-storm).

384 

385### Path Parameters

386 

387* `request_id` (string, required) — The deferred request id returned by a previous image request.

388 

389### Response Body

390 

391* `data` (array | null) — The generated images. Present when done.

392 

393* `error` (object)

394 

395 * `code` (string, required) — Machine-readable error code: \`invalid\_argument\`, \`permission\_denied\`,

396 \`failed\_precondition\`, \`service\_unavailable\` or \`internal\_error\`.

397 

398 * `message` (string, required) — Human-readable error message describing the failure.

399 

400* `request_id` (string, required) — The polled request id.

401 

402* `status` (string, required) — \`"pending"\`, \`"done"\` or \`"failed"\`.

403 

404* `usage` (object)

405 

406 * `cost_in_usd_ticks` (integer, required) — The cost of this request expressed in USD ticks.

407 One USD cent equals 100,000,000 ticks, so one US dollar equals

408 10,000,000,000 ticks.

409 

410 * `input_tokens` (integer | null) — Total input tokens: prompt text tokens + input image tokens

411 (the sum of \`input\_tokens\_details\`, where \`cached\_tokens\` is a subset

412 of \`text\_tokens\`, not additive).

413 

414 * `input_tokens_details` (object)

415 

416 * `cached_tokens` (integer, required) — Text tokens served from cache from previous requests (a subset of

417 \`text\_tokens\`).

418 

419 * `image_tokens` (integer, required) — Input image tokens, as reported by the image engine.

420 

421 * `text_tokens` (integer, required) — Prompt text tokens consumed by the prompt-rewriting (upsampler) LLM,

422 including any served from cache.

423 

424 * `output_tokens` (integer | null) — Total output tokens: rewritten-prompt text tokens + reasoning tokens

425 \+ generated image tokens (the sum of \`output\_tokens\_details\`).

426 

427 * `output_tokens_details` (object)

428 

429 * `image_tokens` (integer, required) — Generated image tokens, as reported by the image engine.

430 

431 * `reasoning_tokens` (integer, required) — Reasoning (thinking) tokens generated by the prompt-rewriting

432 (upsampler) LLM.

433 

434 * `text_tokens` (integer, required) — Rewritten-prompt text tokens generated by the prompt-rewriting

435 (upsampler) LLM, excluding reasoning tokens.

436 

437 * `total_tokens` (integer | null) — Total tokens (input + output).

438 

439### Code Examples

440 

441```bash

442curl -s "https://api.x.ai/v1/images/$IMAGE_REQUEST_ID" \

443 -H "Authorization: Bearer $XAI_API_KEY"

444```

445 

446```javascriptWithoutSDK

447const response = await fetch(

448 `https://api.x.ai/v1/images/${process.env.IMAGE_REQUEST_ID}`,

449 {

450 headers: {

451 Authorization: `Bearer ${process.env.XAI_API_KEY}`,

452 },

453 },

454);

455 

456console.log(JSON.stringify(await response.json(), null, 2));

457```

458 

459```pythonWithoutSDK

460import json

461import os

462 

463import requests

464 

465request_id = os.environ["IMAGE_REQUEST_ID"]

466 

467response = requests.get(

468 f"https://api.x.ai/v1/images/{request_id}",

469 headers={

470 "Authorization": f"Bearer {os.environ['XAI_API_KEY']}",

471 },

472)

473 

474print(json.dumps(response.json(), indent=2))

475```

476 

477\*\*Response example:\*\*

478 

479```json

480{

481 "request_id": "e5b1b4d4-7b6a-4a0e-9c0d-7f3c7d8a1b2c",

482 "status": "done",

483 "data": [

484 {

485 "url": "..."

486 }

487 ],

488 "usage": {

489 "total_tokens": 0,

490 "image_cost": 2000000

491 }

492}

493```

Details

173 173 

174> [!WARNING]174> [!WARNING]

175>175>

176> **Deprecated**: The Anthropic SDK compatibility is fully deprecated. Please migrate to the [Responses API](/developers/rest-api-reference/inference/responses#create-new-response) or [gRPC](/developers/grpc-api-reference).176> **Deprecated**: The Anthropic SDK compatibility is fully deprecated. Please migrate to the [Responses API](/developers/rest-api-reference/inference/responses#create-new-response) or the gRPC API.

177 177 

178## POST /v1/messages178## POST /v1/messages

179 179 


281 281 

282> [!WARNING]282> [!WARNING]

283>283>

284> **Deprecated**: The Anthropic SDK compatibility is fully deprecated. Please migrate to the [Responses API](/developers/rest-api-reference/inference/responses#create-new-response) or [gRPC](/developers/grpc-api-reference).284> **Deprecated**: The Anthropic SDK compatibility is fully deprecated. Please migrate to the [Responses API](/developers/rest-api-reference/inference/responses#create-new-response) or the gRPC API.

285 285 

286## POST /v1/complete286## POST /v1/complete

287 287 

Details

240 240 

241### Code Examples241### Code Examples

242 242 

243```bash

244curl -s https://api.x.ai/v1/responses \

245 -H "Content-Type: application/json" \

246 -H "Authorization: Bearer $XAI_API_KEY" \

247 -d '{

248 "model": "grok-4.7",

249 "input": "What is the meaning of life?"

250 }'

251```

252 

253```javascriptAISDK243```javascriptAISDK

254import { xai } from "@ai-sdk/xai";244import { xai } from "@ai-sdk/xai";

255import { generateText } from "ai";245import { generateText } from "ai";


280print(response.model_dump_json(indent=2))270print(response.model_dump_json(indent=2))

281```271```

282 272 

273```bash

274curl -s https://api.x.ai/v1/responses \

275 -H "Content-Type: application/json" \

276 -H "Authorization: Bearer $XAI_API_KEY" \

277 -d '{

278 "model": "grok-4.7",

279 "input": "What is the meaning of life?"

280 }'

281```

282 

283```javascriptOpenAISDK283```javascriptOpenAISDK

284import OpenAI from "openai";284import OpenAI from "openai";

285 285 


644 644 

645### Code Examples645### Code Examples

646 646 

647```bash

648curl -s https://api.x.ai/v1/responses/compact \

649 -H "Content-Type: application/json" \

650 -H "Authorization: Bearer $XAI_API_KEY" \

651 -d '{

652 "model": "grok-4.7",

653 "input": [

654 {"role": "system", "content": "You are a concise and knowledgeable science tutor."},

655 {"role": "user", "content": "What is the Higgs boson and why is it important?"},

656 {"role": "assistant", "content": "The Higgs boson is an elementary particle in the Standard Model, predicted by Peter Higgs in 1964 and confirmed at CERN in 2012. It is the quantum excitation of the Higgs field, which gives mass to fundamental particles via the Higgs mechanism."},

657 {"role": "user", "content": "How does the Higgs mechanism actually work?"},

658 {"role": "assistant", "content": "Through spontaneous symmetry breaking. The Higgs field has a nonzero vacuum value, and particles acquire mass in proportion to how strongly they couple to it. Photons do not couple, which is why they remain massless."}

659 ]

660 }'

661```

662 

663```pythonOpenAISDK647```pythonOpenAISDK

664import os648import os

665 649 


698print(compacted.model_dump_json(indent=2))682print(compacted.model_dump_json(indent=2))

699```683```

700 684 

685```bash

686curl -s https://api.x.ai/v1/responses/compact \

687 -H "Content-Type: application/json" \

688 -H "Authorization: Bearer $XAI_API_KEY" \

689 -d '{

690 "model": "grok-4.7",

691 "input": [

692 {"role": "system", "content": "You are a concise and knowledgeable science tutor."},

693 {"role": "user", "content": "What is the Higgs boson and why is it important?"},

694 {"role": "assistant", "content": "The Higgs boson is an elementary particle in the Standard Model, predicted by Peter Higgs in 1964 and confirmed at CERN in 2012. It is the quantum excitation of the Higgs field, which gives mass to fundamental particles via the Higgs mechanism."},

695 {"role": "user", "content": "How does the Higgs mechanism actually work?"},

696 {"role": "assistant", "content": "Through spontaneous symmetry breaking. The Higgs field has a nonzero vacuum value, and particles acquire mass in proportion to how strongly they couple to it. Photons do not couple, which is why they remain massless."}

697 ]

698 }'

699```

700 

701```javascriptOpenAISDK701```javascriptOpenAISDK

702import OpenAI from "openai";702import OpenAI from "openai";

703 703 

Details

9 9 

10### Request Body10### Request Body

11 11 

12* `aspect_ratio` ("1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3")12* `aspect_ratio` ("1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "21:9" | "5:2")

13 13 

14* `duration` (integer | null) — Video duration in seconds. Range: \[1, 15]. Default: 8.14* `duration` (integer | null) — Video duration in seconds. Range: \[1, 15]. Default: 8.

15 Also accepts \`seconds\` for OpenAI API compatibility.15 Also accepts \`seconds\` for OpenAI API compatibility.

Details

464| **Extract insights from multiple sources** | Web Search + X Search + Code Execution | Collect data from various sources then compute correlations and trends |464| **Extract insights from multiple sources** | Web Search + X Search + Code Execution | Collect data from various sources then compute correlations and trends |

465| **Monitor real-time discussions** | X Search + Web Search | Track social sentiment alongside authoritative information |465| **Monitor real-time discussions** | X Search + Web Search | Track social sentiment alongside authoritative information |

466 466 

467```pythonXAI

468from xai_sdk.tools import web_search, x_search, code_execution

469 

470# Example tool combinations for different scenarios

471research_setup = [web_search(), code_execution()]

472news_setup = [web_search(), x_search()]

473comprehensive_setup = [web_search(), x_search(), code_execution()]

474```

475 

476```pythonWithoutSDK467```pythonWithoutSDK

477research_setup = {468research_setup = {

478 "tools": [469 "tools": [


497}488}

498```489```

499 490 

491```pythonXAI

492from xai_sdk.tools import web_search, x_search, code_execution

493 

494# Example tool combinations for different scenarios

495research_setup = [web_search(), code_execution()]

496news_setup = [web_search(), x_search()]

497comprehensive_setup = [web_search(), x_search(), code_execution()]

498```

499 

500### Using Tool Combinations in Different Scenarios500### Using Tool Combinations in Different Scenarios

501 501 

5021. When you want to search for news on the Internet, you can activate all search tools:5021. When you want to search for news on the Internet, you can activate all search tools:

503 * Web search tool503 * Web search tool

504 * X search tool504 * X search tool

505 505 

506```pythonXAI

507import os

508 

509from xai_sdk import Client

510from xai_sdk.chat import user

511from xai_sdk.tools import web_search, x_search

512 

513client = Client(api_key=os.getenv("XAI_API_KEY"))

514chat = client.chat.create(

515 model="grok-4.7", # reasoning model

516 tools=[

517 web_search(),

518 x_search(),

519 ],

520 include=["verbose_streaming"],

521)

522 

523chat.append(user("what is the latest update from xAI?"))

524 

525is_thinking = True

526for response, chunk in chat.stream():

527 # View the server-side tool calls as they are being made in real-time

528 for tool_call in chunk.tool_calls:

529 print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")

530 if response.usage.reasoning_tokens and is_thinking:

531 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

532 if chunk.content and is_thinking:

533 print("\\n\\nFinal Response:")

534 is_thinking = False

535 if chunk.content and not is_thinking:

536 print(chunk.content, end="", flush=True)

537 

538print("\\n\\nCitations:")

539print(response.citations)

540print("\\n\\nUsage:")

541print(response.usage)

542print(response.server_side_tool_usage)

543print("\\n\\nServer Side Tool Calls:")

544print(response.tool_calls)

545```

546 

547```pythonOpenAISDK506```pythonOpenAISDK

548import os507import os

549from openai import OpenAI508from openai import OpenAI


628}'587}'

629```588```

630 589 

6312. When you want to collect up-to-date data from the Internet and perform calculations based on the Internet data, you can choose to activate:

632 * Web search tool

633 * Code execution tool

634 

635```pythonXAI590```pythonXAI

636import os591import os

637 592 

638from xai_sdk import Client593from xai_sdk import Client

639from xai_sdk.chat import user594from xai_sdk.chat import user

640from xai_sdk.tools import web_search, code_execution595from xai_sdk.tools import web_search, x_search

641 596 

642client = Client(api_key=os.getenv("XAI_API_KEY"))597client = Client(api_key=os.getenv("XAI_API_KEY"))

643chat = client.chat.create(598chat = client.chat.create(

644 model="grok-4.7", # reasoning model599 model="grok-4.7", # reasoning model

645 # research_tools

646 tools=[600 tools=[

647 web_search(),601 web_search(),

648 code_execution(),602 x_search(),

649 ],603 ],

650 include=["verbose_streaming"],604 include=["verbose_streaming"],

651)605)

652 606 

653chat.append(user("What is the average market cap of the companies with the top 5 market cap in the US stock market today?"))607chat.append(user("what is the latest update from xAI?"))

654 608 

655# sample or stream the response...609is_thinking = True

610for response, chunk in chat.stream():

611 # View the server-side tool calls as they are being made in real-time

612 for tool_call in chunk.tool_calls:

613 print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")

614 if response.usage.reasoning_tokens and is_thinking:

615 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

616 if chunk.content and is_thinking:

617 print("\\n\\nFinal Response:")

618 is_thinking = False

619 if chunk.content and not is_thinking:

620 print(chunk.content, end="", flush=True)

621 

622print("\\n\\nCitations:")

623print(response.citations)

624print("\\n\\nUsage:")

625print(response.usage)

626print(response.server_side_tool_usage)

627print("\\n\\nServer Side Tool Calls:")

628print(response.tool_calls)

656```629```

657 630 

6312. When you want to collect up-to-date data from the Internet and perform calculations based on the Internet data, you can choose to activate:

632 * Web search tool

633 * Code execution tool

634 

658```pythonOpenAISDK635```pythonOpenAISDK

659import os636import os

660from openai import OpenAI637from openai import OpenAI


741}'718}'

742```719```

743 720 

721```pythonXAI

722import os

723 

724from xai_sdk import Client

725from xai_sdk.chat import user

726from xai_sdk.tools import web_search, code_execution

727 

728client = Client(api_key=os.getenv("XAI_API_KEY"))

729chat = client.chat.create(

730 model="grok-4.7", # reasoning model

731 # research_tools

732 tools=[

733 web_search(),

734 code_execution(),

735 ],

736 include=["verbose_streaming"],

737)

738 

739chat.append(user("What is the average market cap of the companies with the top 5 market cap in the US stock market today?"))

740 

741# sample or stream the response...

742```

743 

744## Using Images in the Context744## Using Images in the Context

745 745 

746You can bootstrap your requests with an initial conversation context that includes images.746You can bootstrap your requests with an initial conversation context that includes images.

tools/citations.md +75 −75

Details

53 53 

54#### Enabled (default for Responses API; opt-in for xAI Python SDK)54#### Enabled (default for Responses API; opt-in for xAI Python SDK)

55 55 

56```bash customLanguage="bash" highlightedLines="10"56```javascript customLanguage="javascriptAISDK" highlightedLines="8"

57# Inline citations are enabled by default for the Responses API57import { xai } from '@ai-sdk/xai';

58curl https://api.x.ai/v1/responses \58import { generateText } from 'ai';

59 -H "Content-Type: application/json" \

60 -H "Authorization: Bearer $XAI_API_KEY" \

61 -d '{

62 "model": "grok-4.7",

63 "input": [

64 {"role": "user", "content": "What is xAI?"}

65 ],

66 "tools": [{"type": "web_search"}]

67}'

68```

69 

70```python customLanguage="pythonXAI" highlightedLines="14"

71import os

72 

73from xai_sdk import Client

74from xai_sdk.chat import user

75from xai_sdk.tools import web_search, x_search

76 59 

77client = Client(api_key=os.getenv("XAI_API_KEY"))60const { text, sources } = await generateText({

78chat = client.chat.create(61 model: xai.responses('grok-4.7'),

79 model="grok-4.7",62 prompt: 'What is xAI?',

80 tools=[63 tools: {

81 web_search(),64 web_search: xai.tools.webSearch(), // inline citations are enabled by default

82 x_search(),65 },

83 ],66});

84 include=["inline_citations"], # Enable inline citations (opt-in for xAI Python SDK)

85)

86 67 

87chat.append(user("What is xAI?"))68// Text includes inline citation markdown

88response = chat.sample()69console.log(text);

89 70 

90# Access the response text (includes inline citation markdown)71// Sources contain all citation URLs

91print(response.content)72console.log('Sources:', sources);

92```73```

93 74 

94```python customLanguage="pythonOpenAISDK" highlightedLines="15"75```python customLanguage="pythonOpenAISDK" highlightedLines="15"


118 print(content.text)99 print(content.text)

119```100```

120 101 

121```javascript customLanguage="javascriptAISDK" highlightedLines="8"102```bash customLanguage="bash" highlightedLines="10"

122import { xai } from '@ai-sdk/xai';103# Inline citations are enabled by default for the Responses API

123import { generateText } from 'ai';104curl https://api.x.ai/v1/responses \

124 105 -H "Content-Type: application/json" \

125const { text, sources } = await generateText({106 -H "Authorization: Bearer $XAI_API_KEY" \

126 model: xai.responses('grok-4.7'),107 -d '{

127 prompt: 'What is xAI?',108 "model": "grok-4.7",

128 tools: {109 "input": [

129 web_search: xai.tools.webSearch(), // inline citations are enabled by default110 {"role": "user", "content": "What is xAI?"}

130 },111 ],

131});112 "tools": [{"type": "web_search"}]

132 113}'

133// Text includes inline citation markdown

134console.log(text);

135 

136// Sources contain all citation URLs

137console.log('Sources:', sources);

138```114```

139 115 

140```javascript customLanguage="javascriptOpenAISDK" highlightedLines="13"116```javascript customLanguage="javascriptOpenAISDK" highlightedLines="13"


165}141}

166```142```

167 143 

144```python customLanguage="pythonXAI" highlightedLines="14"

145import os

146 

147from xai_sdk import Client

148from xai_sdk.chat import user

149from xai_sdk.tools import web_search, x_search

150 

151client = Client(api_key=os.getenv("XAI_API_KEY"))

152chat = client.chat.create(

153 model="grok-4.7",

154 tools=[

155 web_search(),

156 x_search(),

157 ],

158 include=["inline_citations"], # Enable inline citations (opt-in for xAI Python SDK)

159)

160 

161chat.append(user("What is xAI?"))

162response = chat.sample()

163 

164# Access the response text (includes inline citation markdown)

165print(response.content)

166```

167 

168#### Disabled (opt-out for Responses API; default for xAI Python SDK)168#### Disabled (opt-out for Responses API; default for xAI Python SDK)

169 169 

170```python customLanguage="pythonOpenAISDK" highlightedLines="17"170```python customLanguage="pythonOpenAISDK" highlightedLines="17"


354 354 

355Image embeds can also produce annotation metadata. The annotation `title` is not shown in the Markdown image.355Image embeds can also produce annotation metadata. The annotation `title` is not shown in the Markdown image.

356 356 

357```python customLanguage="pythonXAI"

358# After streaming or sampling completes, access the structured inline citations:

359for citation in response.inline_citations:

360 print(f"Citation [{citation.id}]:")

361 print(f" Position: {citation.start_index} to {citation.end_index}")

362

363 # Check citation type

364 if citation.HasField("web_citation"):

365 print(f" Web URL: {citation.web_citation.url}")

366 elif citation.HasField("x_citation"):

367 print(f" X URL: {citation.x_citation.url}")

368```

369 

370```python customLanguage="pythonOpenAISDK"

371# Access annotations from the response

372for item in response.output:

373 if item.type == "message":

374 for content in item.content:

375 if content.type == "output_text":

376 for annotation in content.annotations:

377 print(f"Citation [{annotation.title}]:")

378 print(f" URL: {annotation.url}")

379 print(f" Position: {annotation.start_index} to {annotation.end_index}")

380```

381 

382```javascript customLanguage="javascriptAISDK"357```javascript customLanguage="javascriptAISDK"

383import { xai } from '@ai-sdk/xai';358import { xai } from '@ai-sdk/xai';

384import { streamText } from 'ai';359import { streamText } from 'ai';


399}374}

400```375```

401 376 

377```python customLanguage="pythonOpenAISDK"

378# Access annotations from the response

379for item in response.output:

380 if item.type == "message":

381 for content in item.content:

382 if content.type == "output_text":

383 for annotation in content.annotations:

384 print(f"Citation [{annotation.title}]:")

385 print(f" URL: {annotation.url}")

386 print(f" Position: {annotation.start_index} to {annotation.end_index}")

387```

388 

402```javascript customLanguage="javascriptOpenAISDK"389```javascript customLanguage="javascriptOpenAISDK"

403// Access annotations from the response390// Access annotations from the response

404for (const item of response.output) {391for (const item of response.output) {


416}403}

417```404```

418 405 

406```python customLanguage="pythonXAI"

407# After streaming or sampling completes, access the structured inline citations:

408for citation in response.inline_citations:

409 print(f"Citation [{citation.id}]:")

410 print(f" Position: {citation.start_index} to {citation.end_index}")

411

412 # Check citation type

413 if citation.HasField("web_citation"):

414 print(f" Web URL: {citation.web_citation.url}")

415 elif citation.HasField("x_citation"):

416 print(f" X URL: {citation.x_citation.url}")

417```

418 

419```output419```output

420Citation [1]:420Citation [1]:

421 Position: 37 to 76421 Position: 37 to 76

Details

39 39 

40### Basic Calculations40### Basic Calculations

41 41 

42```pythonXAI42```javascriptAISDK

43import os43import { xai } from '@ai-sdk/xai';

44 44import { generateText } from 'ai';

45from xai_sdk import Client

46from xai_sdk.chat import user

47from xai_sdk.tools import code_execution

48 

49client = Client(api_key=os.getenv("XAI_API_KEY"))

50chat = client.chat.create(

51 model="grok-4.7", # reasoning model

52 tools=[code_execution()],

53 include=["verbose_streaming"],

54)

55 

56# Ask for a mathematical calculation

57chat.append(user("Calculate the compound interest for $10,000 at 5% annually for 10 years"))

58 45 

59is_thinking = True46const { text } = await generateText({

60for response, chunk in chat.stream():47 model: xai.responses('grok-4.7'),

61 # View the server-side tool calls as they are being made in real-time48 prompt: 'Calculate the compound interest for $10,000 at 5% annually for 10 years',

62 for tool_call in chunk.tool_calls:49 tools: {

63 print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")50 code_execution: xai.tools.codeExecution(),

64 if response.usage.reasoning_tokens and is_thinking:51 },

65 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)52});

66 if chunk.content and is_thinking:

67 print("\\n\\nFinal Response:")

68 is_thinking = False

69 if chunk.content and not is_thinking:

70 print(chunk.content, end="", flush=True)

71 53 

72print("\\n\\nCitations:")54console.log(text);

73print(response.citations)

74print("\\n\\nUsage:")

75print(response.usage)

76print(response.server_side_tool_usage)

77print("\\n\\nServer Side Tool Calls:")

78print(response.tool_calls)

79```55```

80 56 

81```pythonOpenAISDK57```pythonOpenAISDK


153}'129}'

154```130```

155 131 

132```pythonXAI

133import os

134 

135from xai_sdk import Client

136from xai_sdk.chat import user

137from xai_sdk.tools import code_execution

138 

139client = Client(api_key=os.getenv("XAI_API_KEY"))

140chat = client.chat.create(

141 model="grok-4.7", # reasoning model

142 tools=[code_execution()],

143 include=["verbose_streaming"],

144)

145 

146# Ask for a mathematical calculation

147chat.append(user("Calculate the compound interest for $10,000 at 5% annually for 10 years"))

148 

149is_thinking = True

150for response, chunk in chat.stream():

151 # View the server-side tool calls as they are being made in real-time

152 for tool_call in chunk.tool_calls:

153 print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")

154 if response.usage.reasoning_tokens and is_thinking:

155 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

156 if chunk.content and is_thinking:

157 print("\\n\\nFinal Response:")

158 is_thinking = False

159 if chunk.content and not is_thinking:

160 print(chunk.content, end="", flush=True)

161 

162print("\\n\\nCitations:")

163print(response.citations)

164print("\\n\\nUsage:")

165print(response.usage)

166print(response.server_side_tool_usage)

167print("\\n\\nServer Side Tool Calls:")

168print(response.tool_calls)

169```

170 

171### Data Analysis

172 

156```javascriptAISDK173```javascriptAISDK

157import { xai } from '@ai-sdk/xai';174import { xai } from '@ai-sdk/xai';

158import { generateText } from 'ai';175import { generateText } from 'ai';

159 176 

160const { text } = await generateText({177// Step 1: Load and analyze data

178const step1 = await generateText({

161 model: xai.responses('grok-4.7'),179 model: xai.responses('grok-4.7'),

162 prompt: 'Calculate the compound interest for $10,000 at 5% annually for 10 years',180 prompt: \`I have sales data for Q1-Q4: [120000, 135000, 98000, 156000].

181Please analyze this data and create a visualization showing:

1821. Quarterly trends

1832. Growth rates

1843. Statistical summary\`,

163 tools: {185 tools: {

164 code_execution: xai.tools.codeExecution(),186 code_execution: xai.tools.codeExecution(),

165 },187 },

166});188});

167 189 

168console.log(text);190console.log('##### Step 1: Data Analysis #####');

169```191console.log(step1.text);

170 192 

171### Data Analysis193// Step 2: Follow-up analysis using previousResponseId

194const step2 = await generateText({

195 model: xai.responses('grok-4.7'),

196 prompt: 'Now predict Q1 next year using linear regression',

197 tools: {

198 code_execution: xai.tools.codeExecution(),

199 },

200 providerOptions: {

201 xai: {

202 previousResponseId: step1.response.id,

203 },

204 },

205});

206 

207console.log('##### Step 2: Prediction Analysis #####');

208console.log(step2.text);

209```

172 210 

173```pythonXAI211```pythonXAI

174import os212import os


244print(response.tool_calls)282print(response.tool_calls)

245```283```

246 284 

247```javascriptAISDK

248import { xai } from '@ai-sdk/xai';

249import { generateText } from 'ai';

250 

251// Step 1: Load and analyze data

252const step1 = await generateText({

253 model: xai.responses('grok-4.7'),

254 prompt: \`I have sales data for Q1-Q4: [120000, 135000, 98000, 156000].

255Please analyze this data and create a visualization showing:

2561. Quarterly trends

2572. Growth rates

2583. Statistical summary\`,

259 tools: {

260 code_execution: xai.tools.codeExecution(),

261 },

262});

263 

264console.log('##### Step 1: Data Analysis #####');

265console.log(step1.text);

266 

267// Step 2: Follow-up analysis using previousResponseId

268const step2 = await generateText({

269 model: xai.responses('grok-4.7'),

270 prompt: 'Now predict Q1 next year using linear regression',

271 tools: {

272 code_execution: xai.tools.codeExecution(),

273 },

274 providerOptions: {

275 xai: {

276 previousResponseId: step1.response.id,

277 },

278 },

279});

280 

281console.log('##### Step 2: Prediction Analysis #####');

282console.log(step2.text);

283```

284 

285## Best Practices285## Best Practices

286 286 

287### 1. **Be Specific in Requests**287### 1. **Be Specific in Requests**

Details

16 16 

17## Quick Start17## Quick Start

18 18 

19```javascriptAISDK

20import { xai } from '@ai-sdk/xai';

21import { streamText, tool, stepCountIs } from 'ai';

22import { z } from 'zod';

23 

24const result = streamText({

25 model: xai.responses('grok-4.7'),

26 tools: {

27 getTemperature: tool({

28 description: 'Get current temperature for a location',

29 inputSchema: z.object({

30 location: z.string().describe('City name'),

31 unit: z.enum(['celsius', 'fahrenheit']).default('fahrenheit'),

32 }),

33 execute: async ({ location, unit }) => ({

34 location,

35 temperature: unit === 'fahrenheit' ? 59 : 15,

36 unit,

37 }),

38 }),

39 },

40 stopWhen: stepCountIs(5),

41 prompt: 'What is the temperature in San Francisco?',

42});

43 

44for await (const chunk of result.fullStream) {

45 if (chunk.type === 'text-delta') {

46 process.stdout.write(chunk.text);

47 }

48}

49```

50 

51```pythonOpenAISDK

52import os

53import json

54from openai import OpenAI

55 

56client = OpenAI(

57 api_key=os.getenv("XAI_API_KEY"),

58 base_url="https://api.x.ai/v1",

59)

60 

61tools = [

62 {

63 "type": "function",

64 "name": "get_temperature",

65 "description": "Get current temperature for a location",

66 "parameters": {

67 "type": "object",

68 "properties": {

69 "location": {"type": "string", "description": "City name"},

70 "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"}

71 },

72 "required": ["location"]

73 },

74 },

75]

76 

77response = client.responses.create(

78 model="grok-4.7",

79 input=[{"role": "user", "content": "What is the temperature in San Francisco?"}],

80 tools=tools,

81)

82 

83# Handle function calls

84for item in response.output:

85 if item.type == "function_call":

86 args = json.loads(item.arguments)

87 result = {"location": args["location"], "temperature": 59, "unit": args.get("unit", "fahrenheit")}

88 

89 response = client.responses.create(

90 model="grok-4.7",

91 input=[{"type": "function_call_output", "call_id": item.call_id, "output": json.dumps(result)}],

92 tools=tools,

93 previous_response_id=response.id,

94 )

95 

96for item in response.output:

97 if item.type == "message":

98 print(item.content[0].text)

99```

100 

19```bash customLanguage="bash"101```bash customLanguage="bash"

20curl https://api.x.ai/v1/responses \102curl https://api.x.ai/v1/responses \

21 -H "Content-Type: application/json" \103 -H "Content-Type: application/json" \


89print(response.content)171print(response.content)

90```172```

91 173 

174## Defining Tools with Pydantic

175 

176Use Pydantic models for type-safe parameter schemas:

177 

92```pythonOpenAISDK178```pythonOpenAISDK

93import os179from typing import Literal

94import json180from pydantic import BaseModel, Field

95from openai import OpenAI

96 181 

97client = OpenAI(182class TemperatureRequest(BaseModel):

98 api_key=os.getenv("XAI_API_KEY"),183 location: str = Field(description="City and state, e.g. San Francisco, CA")

99 base_url="https://api.x.ai/v1",184 unit: Literal["celsius", "fahrenheit"] = Field("fahrenheit", description="Temperature unit")

100)185 

186class CeilingRequest(BaseModel):

187 location: str = Field(description="City and state, e.g. San Francisco, CA")

101 188 

102tools = [189tools = [

103 {190 {

104 "type": "function",191 "type": "function",

105 "name": "get_temperature",192 "name": "get_temperature",

106 "description": "Get current temperature for a location",193 "description": "Get current temperature for a location",

107 "parameters": {194 "parameters": TemperatureRequest.model_json_schema(),

108 "type": "object",

109 "properties": {

110 "location": {"type": "string", "description": "City name"},

111 "unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "fahrenheit"}

112 },

113 "required": ["location"]

114 },195 },

196 {

197 "type": "function",

198 "name": "get_ceiling",

199 "description": "Get current cloud ceiling for a location",

200 "parameters": CeilingRequest.model_json_schema(),

115 },201 },

116]202]

117 

118response = client.responses.create(

119 model="grok-4.7",

120 input=[{"role": "user", "content": "What is the temperature in San Francisco?"}],

121 tools=tools,

122)

123 

124# Handle function calls

125for item in response.output:

126 if item.type == "function_call":

127 args = json.loads(item.arguments)

128 result = {"location": args["location"], "temperature": 59, "unit": args.get("unit", "fahrenheit")}

129 

130 response = client.responses.create(

131 model="grok-4.7",

132 input=[{"type": "function_call_output", "call_id": item.call_id, "output": json.dumps(result)}],

133 tools=tools,

134 previous_response_id=response.id,

135 )

136 

137for item in response.output:

138 if item.type == "message":

139 print(item.content[0].text)

140```

141 

142```javascriptAISDK

143import { xai } from '@ai-sdk/xai';

144import { streamText, tool, stepCountIs } from 'ai';

145import { z } from 'zod';

146 

147const result = streamText({

148 model: xai.responses('grok-4.7'),

149 tools: {

150 getTemperature: tool({

151 description: 'Get current temperature for a location',

152 inputSchema: z.object({

153 location: z.string().describe('City name'),

154 unit: z.enum(['celsius', 'fahrenheit']).default('fahrenheit'),

155 }),

156 execute: async ({ location, unit }) => ({

157 location,

158 temperature: unit === 'fahrenheit' ? 59 : 15,

159 unit,

160 }),

161 }),

162 },

163 stopWhen: stepCountIs(5),

164 prompt: 'What is the temperature in San Francisco?',

165});

166 

167for await (const chunk of result.fullStream) {

168 if (chunk.type === 'text-delta') {

169 process.stdout.write(chunk.text);

170 }

171}

172```203```

173 204 

174## Defining Tools with Pydantic

175 

176Use Pydantic models for type-safe parameter schemas:

177 

178```pythonXAI205```pythonXAI

179from typing import Literal206from typing import Literal

180from pydantic import BaseModel, Field207from pydantic import BaseModel, Field


202]229]

203```230```

204 231 

232## Handling Tool Calls

233 

234When the model wants to use your tool, execute the function and return the result:

235 

205```pythonOpenAISDK236```pythonOpenAISDK

206from typing import Literal237import json

207from pydantic import BaseModel, Field

208 238 

209class TemperatureRequest(BaseModel):239def get_temperature(location: str, unit: str = "fahrenheit") -> dict:

210 location: str = Field(description="City and state, e.g. San Francisco, CA")240 temp = 59 if unit == "fahrenheit" else 15

211 unit: Literal["celsius", "fahrenheit"] = Field("fahrenheit", description="Temperature unit")241 return {"location": location, "temperature": temp, "unit": unit}

212 242 

213class CeilingRequest(BaseModel):243tools_map = {"get_temperature": get_temperature}

214 location: str = Field(description="City and state, e.g. San Francisco, CA")

215 244 

216tools = [245# Process function calls

217 {246for item in response.output:

218 "type": "function",247 if item.type == "function_call":

219 "name": "get_temperature",248 name = item.name

220 "description": "Get current temperature for a location",249 args = json.loads(item.arguments)

221 "parameters": TemperatureRequest.model_json_schema(),

222 },

223 {

224 "type": "function",

225 "name": "get_ceiling",

226 "description": "Get current cloud ceiling for a location",

227 "parameters": CeilingRequest.model_json_schema(),

228 },

229]

230```

231 250 

232## Handling Tool Calls251 if name not in tools_map:

252 output = json.dumps({"error": f"Unknown function: {name}"})

253 else:

254 output = json.dumps(tools_map[name](**args))

233 255 

234When the model wants to use your tool, execute the function and return the result:256 response = client.responses.create(

257 model="grok-4.7",

258 input=[{"type": "function_call_output", "call_id": item.call_id, "output": output}],

259 tools=tools,

260 previous_response_id=response.id,

261 )

262 

263for item in response.output:

264 if item.type == "message":

265 print(item.content[0].text)

266```

235 267 

236```pythonXAI268```pythonXAI

237import json269import json


268print(response.content)300print(response.content)

269```301```

270 302 

271```pythonOpenAISDK

272import json

273 

274def get_temperature(location: str, unit: str = "fahrenheit") -> dict:

275 temp = 59 if unit == "fahrenheit" else 15

276 return {"location": location, "temperature": temp, "unit": unit}

277 

278tools_map = {"get_temperature": get_temperature}

279 

280# Process function calls

281for item in response.output:

282 if item.type == "function_call":

283 name = item.name

284 args = json.loads(item.arguments)

285 

286 if name not in tools_map:

287 output = json.dumps({"error": f"Unknown function: {name}"})

288 else:

289 output = json.dumps(tools_map[name](**args))

290 

291 response = client.responses.create(

292 model="grok-4.7",

293 input=[{"type": "function_call_output", "call_id": item.call_id, "output": output}],

294 tools=tools,

295 previous_response_id=response.id,

296 )

297 

298for item in response.output:

299 if item.type == "message":

300 print(item.content[0].text)

301```

302 

303## Combining with Built-in Tools303## Combining with Built-in Tools

304 304 

305Function calling works alongside built-in agentic tools. The model can use web search, then call your custom function:305Function calling works alongside built-in agentic tools. The model can use web search, then call your custom function:

306 306 

307```pythonOpenAISDK

308tools = [

309 {"type": "web_search"}, # Built-in

310 {"type": "x_search"}, # Built-in

311 { # Custom

312 "type": "function",

313 "name": "save_to_database",

314 "description": "Save research results to the database",

315 "parameters": {

316 "type": "object",

317 "properties": {

318 "data": {"type": "string", "description": "Data to save"}

319 },

320 "required": ["data"]

321 },

322 },

323]

324```

325 

307```pythonXAI326```pythonXAI

308from xai_sdk.chat import tool327from xai_sdk.chat import tool

309from xai_sdk.tools import web_search, x_search328from xai_sdk.tools import web_search, x_search


330)349)

331```350```

332 351 

333```pythonOpenAISDK

334tools = [

335 {"type": "web_search"}, # Built-in

336 {"type": "x_search"}, # Built-in

337 { # Custom

338 "type": "function",

339 "name": "save_to_database",

340 "description": "Save research results to the database",

341 "parameters": {

342 "type": "object",

343 "properties": {

344 "data": {"type": "string", "description": "Data to save"}

345 },

346 "required": ["data"]

347 },

348 },

349]

350```

351 

352When mixing tools:352When mixing tools:

353 353 

354* **Built-in tools** execute automatically on SpaceXAI servers354* **Built-in tools** execute automatically on SpaceXAI servers

Details

19 19 

20Add `image_generation` to `tools` and ask for an image. In the xAI SDK, each generated image is exposed on `response.image_outputs` as decoded bytes you can write straight to a file. In the Responses API, each image arrives as an `image_generation_call` output item whose `result` field carries the base64-encoded image with no data-URL prefix, so you can decode it directly.20Add `image_generation` to `tools` and ask for an image. In the xAI SDK, each generated image is exposed on `response.image_outputs` as decoded bytes you can write straight to a file. In the Responses API, each image arrives as an `image_generation_call` output item whose `result` field carries the base64-encoded image with no data-URL prefix, so you can decode it directly.

21 21 

22```bash customLanguage="bash"

23curl https://api.x.ai/v1/responses \

24 -H "Content-Type: application/json" \

25 -H "Authorization: Bearer $XAI_API_KEY" \

26 -d '{

27 "model": "grok-4.7",

28 "input": "Generate an image of a corgi surfing a big wave, in the style of a Japanese woodblock print",

29 "tools": [

30 {

31 "type": "image_generation"

32 }

33 ]

34}' | jq -r '.output[] | select(.type == "image_generation_call") | .result' \

35 | base64 --decode > corgi_surfing.jpg

36```

37 

38```python customLanguage="pythonXAI"

39import os

40 

41from xai_sdk import Client

42from xai_sdk.chat import user

43from xai_sdk.tools import image_generation

44 

45client = Client(api_key=os.getenv("XAI_API_KEY"))

46 

47chat = client.chat.create(

48 model="grok-4.7",

49 tools=[image_generation()],

50)

51chat.append(user("Generate an image of a corgi surfing a big wave, in the style of a Japanese woodblock print"))

52response = chat.sample()

53 

54print(response.content)

55with open("image.jpeg", "wb") as f:

56 f.write(response.image_outputs[0].image)

57```

58 

59```python customLanguage="pythonOpenAISDK"22```python customLanguage="pythonOpenAISDK"

60import base6423import base64

61import os24import os


84 f.write(base64.b64decode(image_data[0]))47 f.write(base64.b64decode(image_data[0]))

85```48```

86 49 

50```bash customLanguage="bash"

51curl https://api.x.ai/v1/responses \

52 -H "Content-Type: application/json" \

53 -H "Authorization: Bearer $XAI_API_KEY" \

54 -d '{

55 "model": "grok-4.7",

56 "input": "Generate an image of a corgi surfing a big wave, in the style of a Japanese woodblock print",

57 "tools": [

58 {

59 "type": "image_generation"

60 }

61 ]

62}' | jq -r '.output[] | select(.type == "image_generation_call") | .result' \

63 | base64 --decode > corgi_surfing.jpg

64```

65 

87```python customLanguage="pythonRequests"66```python customLanguage="pythonRequests"

88import base6467import base64

89import os68import os


134}113}

135```114```

136 115 

116```python customLanguage="pythonXAI"

117import os

118 

119from xai_sdk import Client

120from xai_sdk.chat import user

121from xai_sdk.tools import image_generation

122 

123client = Client(api_key=os.getenv("XAI_API_KEY"))

124 

125chat = client.chat.create(

126 model="grok-4.7",

127 tools=[image_generation()],

128)

129chat.append(user("Generate an image of a corgi surfing a big wave, in the style of a Japanese woodblock print"))

130response = chat.sample()

131 

132print(response.content)

133with open("image.jpeg", "wb") as f:

134 f.write(response.image_outputs[0].image)

135```

136 

137A completed `image_generation_call` output item looks like this:137A completed `image_generation_call` output item looks like this:

138 138 

139```json139```json


162 162 

163For example, to let the model create images but never modify ones already in the conversation:163For example, to let the model create images but never modify ones already in the conversation:

164 164 

165```python customLanguage="pythonOpenAISDK"

166response = client.responses.create(

167 model="grok-4.7",

168 input="Generate an image of a hot air balloon over the desert",

169 tools=[{"type": "image_generation", "action": "generate"}],

170)

171```

172 

165```bash customLanguage="bash"173```bash customLanguage="bash"

166curl https://api.x.ai/v1/responses \174curl https://api.x.ai/v1/responses \

167 -H "Content-Type: application/json" \175 -H "Content-Type: application/json" \


187response = chat.sample()195response = chat.sample()

188```196```

189 197 

198## Editing input images

199 

200With `action` set to `edit` (or the default `auto`), the model can edit any image already in the conversation: images you attach as input as well as images it generated earlier. Edits produce `image_generation_call` items with an `ie_` ID prefix.

201 

190```python customLanguage="pythonOpenAISDK"202```python customLanguage="pythonOpenAISDK"

203import base64

204import os

205 

206from openai import OpenAI

207 

208client = OpenAI(

209 api_key=os.getenv("XAI_API_KEY"),

210 base_url="https://api.x.ai/v1",

211)

212 

191response = client.responses.create(213response = client.responses.create(

192 model="grok-4.7",214 model="grok-4.7",

193 input="Generate an image of a hot air balloon over the desert",215 input=[

194 tools=[{"type": "image_generation", "action": "generate"}],216 {

217 "role": "user",

218 "content": [

219 {

220 "type": "input_text",

221 "text": "Edit this image so it looks like a watercolor painting.",

222 },

223 {

224 "type": "input_image",

225 "image_url": "https://docs.x.ai/assets/api-examples/images/style-realistic.png",

226 },

227 ],

228 }

229 ],

230 tools=[{"type": "image_generation", "action": "edit"}],

195)231)

196```

197 232 

198## Editing input images233image_data = [

234 output.result

235 for output in response.output

236 if output.type == "image_generation_call"

237]

199 238 

200With `action` set to `edit` (or the default `auto`), the model can edit any image already in the conversation: images you attach as input as well as images it generated earlier. Edits produce `image_generation_call` items with an `ie_` ID prefix.239if image_data:

240 with open("watercolor.jpg", "wb") as f:

241 f.write(base64.b64decode(image_data[0]))

242```

201 243 

202```bash customLanguage="bash"244```bash customLanguage="bash"

203curl https://api.x.ai/v1/responses \245curl https://api.x.ai/v1/responses \


254 f.write(response.image_outputs[0].image)296 f.write(response.image_outputs[0].image)

255```297```

256 298 

299## Multi-turn editing

300 

301Images generated on a previous turn stay editable on follow-up turns. Continue the conversation — append the previous response to the chat in the xAI SDK, or pass `previous_response_id` in the Responses API — and the model can refine its earlier images by reference:

302 

257```python customLanguage="pythonOpenAISDK"303```python customLanguage="pythonOpenAISDK"

258import base64304import base64

259import os305import os


267 313 

268response = client.responses.create(314response = client.responses.create(

269 model="grok-4.7",315 model="grok-4.7",

270 input=[316 input="Generate an image of a lighthouse on a rocky coast",

271 {317 tools=[{"type": "image_generation"}],

272 "role": "user",

273 "content": [

274 {

275 "type": "input_text",

276 "text": "Edit this image so it looks like a watercolor painting.",

277 },

278 {

279 "type": "input_image",

280 "image_url": "https://docs.x.ai/assets/api-examples/images/style-realistic.png",

281 },

282 ],

283 }

284 ],

285 tools=[{"type": "image_generation", "action": "edit"}],

286)318)

287 319 

288image_data = [320image_data = [


292]324]

293 325 

294if image_data:326if image_data:

295 with open("watercolor.jpg", "wb") as f:327 with open("lighthouse.jpg", "wb") as f:

296 f.write(base64.b64decode(image_data[0]))328 f.write(base64.b64decode(image_data[0]))

297```

298 329 

299## Multi-turn editing330# Follow up: edit the image from the previous turn

331followup = client.responses.create(

332 model="grok-4.7",

333 previous_response_id=response.id,

334 input="Make it night time with a full moon",

335 tools=[{"type": "image_generation"}],

336)

300 337 

301Images generated on a previous turn stay editable on follow-up turns. Continue the conversation — append the previous response to the chat in the xAI SDK, or pass `previous_response_id` in the Responses API — and the model can refine its earlier images by reference:338image_data_followup = [

339 output.result

340 for output in followup.output

341 if output.type == "image_generation_call"

342]

343 

344if image_data_followup:

345 with open("lighthouse_night.jpg", "wb") as f:

346 f.write(base64.b64decode(image_data_followup[0]))

347```

302 348 

303```python customLanguage="pythonXAI"349```python customLanguage="pythonXAI"

304import os350import os


328 f.write(followup.image_outputs[0].image)374 f.write(followup.image_outputs[0].image)

329```375```

330 376 

377If you manage conversation state yourself instead of using `previous_response_id`, pass the previous turn's output items (including the `image_generation_call` items) back verbatim in `input`; the images they carry remain editable on the next request.

378 

379## Combining with other tools

380 

381The image generation tool composes with the other server-side tools. Include several tools in the same request and the model orchestrates them within a single agentic loop, feeding what one tool found into the next. Here it looks up a fact with [web search](/developers/tools/web-search) first, then writes an image prompt from what it learned:

382 

331```python customLanguage="pythonOpenAISDK"383```python customLanguage="pythonOpenAISDK"

332import base64384import base64

333import os385import os


341 393 

342response = client.responses.create(394response = client.responses.create(

343 model="grok-4.7",395 model="grok-4.7",

344 input="Generate an image of a lighthouse on a rocky coast",396 input=(

345 tools=[{"type": "image_generation"}],397 "Find out which team won the most recent FIFA World Cup, then generate an "

346)398 "image of a celebratory poster for that team, in a vintage travel-poster style."

347 399 ),

348image_data = [400 tools=[

349 output.result401 {"type": "web_search"},

350 for output in response.output402 {"type": "image_generation"},

351 if output.type == "image_generation_call"403 ],

352]

353 

354if image_data:

355 with open("lighthouse.jpg", "wb") as f:

356 f.write(base64.b64decode(image_data[0]))

357 

358# Follow up: edit the image from the previous turn

359followup = client.responses.create(

360 model="grok-4.7",

361 previous_response_id=response.id,

362 input="Make it night time with a full moon",

363 tools=[{"type": "image_generation"}],

364)404)

365 405 

366image_data_followup = [406for output in response.output:

367 output.result407 if output.type == "web_search_call":

368 for output in followup.output408 print(f"Web search: {output.action}")

369 if output.type == "image_generation_call"409 elif output.type == "image_generation_call":

370]410 print(f"Image prompt: {output.prompt}")

371 411 with open("champions_poster.jpg", "wb") as f:

372if image_data_followup:412 f.write(base64.b64decode(output.result))

373 with open("lighthouse_night.jpg", "wb") as f:413 elif output.type == "message":

374 f.write(base64.b64decode(image_data_followup[0]))414 print(output.content[0].text)

375```415```

376 416 

377If you manage conversation state yourself instead of using `previous_response_id`, pass the previous turn's output items (including the `image_generation_call` items) back verbatim in `input`; the images they carry remain editable on the next request.

378 

379## Combining with other tools

380 

381The image generation tool composes with the other server-side tools. Include several tools in the same request and the model orchestrates them within a single agentic loop, feeding what one tool found into the next. Here it looks up a fact with [web search](/developers/tools/web-search) first, then writes an image prompt from what it learned:

382 

383```bash customLanguage="bash"417```bash customLanguage="bash"

384curl https://api.x.ai/v1/responses \418curl https://api.x.ai/v1/responses \

385 -H "Content-Type: application/json" \419 -H "Content-Type: application/json" \


428print(response.server_side_tool_usage)462print(response.server_side_tool_usage)

429```463```

430 464 

431```python customLanguage="pythonOpenAISDK"

432import base64

433import os

434 

435from openai import OpenAI

436 

437client = OpenAI(

438 api_key=os.getenv("XAI_API_KEY"),

439 base_url="https://api.x.ai/v1",

440)

441 

442response = client.responses.create(

443 model="grok-4.7",

444 input=(

445 "Find out which team won the most recent FIFA World Cup, then generate an "

446 "image of a celebratory poster for that team, in a vintage travel-poster style."

447 ),

448 tools=[

449 {"type": "web_search"},

450 {"type": "image_generation"},

451 ],

452)

453 

454for output in response.output:

455 if output.type == "web_search_call":

456 print(f"Web search: {output.action}")

457 elif output.type == "image_generation_call":

458 print(f"Image prompt: {output.prompt}")

459 with open("champions_poster.jpg", "wb") as f:

460 f.write(base64.b64decode(output.result))

461 elif output.type == "message":

462 print(output.content[0].text)

463```

464 

465The response output interleaves the tool calls in the order they ran: a `web_search_call` item, a message answering the factual question with citations, and an `image_generation_call` item carrying the poster.465The response output interleaves the tool calls in the order they ran: a `web_search_call` item, a message answering the factual question with citations, and an `image_generation_call` item carrying the poster.

466 466 

467The same pattern works with [X search](/developers/tools/x-search), [code execution](/developers/tools/code-execution), and your own client-side functions. See [Advanced Usage](/developers/tools/advanced-usage#tool-combinations) for more tool combination patterns.467The same pattern works with [X search](/developers/tools/x-search), [code execution](/developers/tools/code-execution), and your own client-side functions. See [Advanced Usage](/developers/tools/advanced-usage#tool-combinations) for more tool combination patterns.


472 472 

473In the xAI SDK, pass `include=["verbose_streaming"]` to watch tool calls as they happen; the decoded images are available on the accumulated response via `response.image_outputs` once the stream ends.473In the xAI SDK, pass `include=["verbose_streaming"]` to watch tool calls as they happen; the decoded images are available on the accumulated response via `response.image_outputs` once the stream ends.

474 474 

475```python customLanguage="pythonXAI"

476import os

477 

478from xai_sdk import Client

479from xai_sdk.chat import user

480from xai_sdk.tools import get_tool_call_type, image_generation

481 

482client = Client(api_key=os.getenv("XAI_API_KEY"))

483 

484chat = client.chat.create(

485 model="grok-4.7",

486 tools=[image_generation()],

487 include=["verbose_streaming"],

488)

489chat.append(user("Generate an image of an origami fox in a paper forest"))

490 

491for response, chunk in chat.stream():

492 for tool_call in chunk.tool_calls:

493 if get_tool_call_type(tool_call) == "image_generation_tool":

494 print(f"\nGenerating image: {tool_call.function.arguments}")

495 if chunk.content:

496 print(chunk.content, end="", flush=True)

497 

498# The accumulated response carries the decoded images once the stream ends

499with open("image.jpeg", "wb") as f:

500 f.write(response.image_outputs[0].image)

501```

502 

503```python customLanguage="pythonOpenAISDK"475```python customLanguage="pythonOpenAISDK"

504import base64476import base64

505import os477import os


565}537}

566```538```

567 539 

540```python customLanguage="pythonXAI"

541import os

542 

543from xai_sdk import Client

544from xai_sdk.chat import user

545from xai_sdk.tools import get_tool_call_type, image_generation

546 

547client = Client(api_key=os.getenv("XAI_API_KEY"))

548 

549chat = client.chat.create(

550 model="grok-4.7",

551 tools=[image_generation()],

552 include=["verbose_streaming"],

553)

554chat.append(user("Generate an image of an origami fox in a paper forest"))

555 

556for response, chunk in chat.stream():

557 for tool_call in chunk.tool_calls:

558 if get_tool_call_type(tool_call) == "image_generation_tool":

559 print(f"\nGenerating image: {tool_call.function.arguments}")

560 if chunk.content:

561 print(chunk.content, end="", flush=True)

562 

563# The accumulated response carries the decoded images once the stream ends

564with open("image.jpeg", "wb") as f:

565 f.write(response.image_outputs[0].image)

566```

567 

568## Related568## Related

569 569 

570* [Image Generation](/developers/model-capabilities/images/generation) — Generate images directly with the images endpoint570* [Image Generation](/developers/model-capabilities/images/generation) — Generate images directly with the images endpoint

tools/overview.md +63 −63

Details

33 33 

34## Quick Start34## Quick Start

35 35 

36```bash customLanguage="bash"36```javascriptAISDK

37curl https://api.x.ai/v1/responses \37import { xai } from '@ai-sdk/xai';

38 -H "Content-Type: application/json" \38import { streamText } from 'ai';

39 -H "Authorization: Bearer $XAI_API_KEY" \

40 -d '{

41 "model": "grok-4.7",

42 "stream": true,

43 "input": [

44 {

45 "role": "user",

46 "content": "What are the latest updates from xAI?"

47 }

48 ],

49 "tools": [

50 { "type": "web_search" },

51 { "type": "x_search" },

52 { "type": "code_interpreter" }

53 ]

54}'

55```

56 

57```pythonXAI

58import os

59 

60from xai_sdk import Client

61from xai_sdk.chat import user

62from xai_sdk.tools import web_search, x_search, code_execution

63 

64client = Client(api_key=os.getenv("XAI_API_KEY"))

65chat = client.chat.create(

66 model="grok-4.7",

67 tools=[

68 web_search(),

69 x_search(),

70 code_execution(),

71 ],

72)

73 

74chat.append(user("What are the latest updates from xAI?"))

75 39 

76for response, chunk in chat.stream():40const { fullStream } = streamText({

77 if chunk.content:41 model: xai.responses('grok-4.7'),

78 print(chunk.content, end="", flush=True)42 prompt: 'What are the latest updates from xAI?',

43 tools: {

44 web_search: xai.tools.webSearch(),

45 x_search: xai.tools.xSearch(),

46 code_execution: xai.tools.codeExecution(),

47 },

48});

79 49 

80print("\nCitations:", response.citations)50for await (const part of fullStream) {

51 if (part.type === 'text-delta') {

52 process.stdout.write(part.text);

53 } else if (part.type === 'source' && part.sourceType === 'url') {

54 console.log(`Citation: ${part.url}`);

55 }

56}

81```57```

82 58 

83```pythonOpenAISDK59```pythonOpenAISDK


107 print(event.delta, end="", flush=True)83 print(event.delta, end="", flush=True)

108```84```

109 85 

110```javascriptAISDK86```bash customLanguage="bash"

111import { xai } from '@ai-sdk/xai';87curl https://api.x.ai/v1/responses \

112import { streamText } from 'ai';88 -H "Content-Type: application/json" \

113 89 -H "Authorization: Bearer $XAI_API_KEY" \

114const { fullStream } = streamText({90 -d '{

115 model: xai.responses('grok-4.7'),91 "model": "grok-4.7",

116 prompt: 'What are the latest updates from xAI?',92 "stream": true,

117 tools: {93 "input": [

118 web_search: xai.tools.webSearch(),94 {

119 x_search: xai.tools.xSearch(),95 "role": "user",

120 code_execution: xai.tools.codeExecution(),96 "content": "What are the latest updates from xAI?"

121 },

122});

123 

124for await (const part of fullStream) {

125 if (part.type === 'text-delta') {

126 process.stdout.write(part.text);

127 } else if (part.type === 'source' && part.sourceType === 'url') {

128 console.log(`Citation: ${part.url}`);

129 }97 }

130}98 ],

99 "tools": [

100 { "type": "web_search" },

101 { "type": "x_search" },

102 { "type": "code_interpreter" }

103 ]

104}'

131```105```

132 106 

133```javascriptOpenAISDK107```javascriptOpenAISDK


158}132}

159```133```

160 134 

135```pythonXAI

136import os

137 

138from xai_sdk import Client

139from xai_sdk.chat import user

140from xai_sdk.tools import web_search, x_search, code_execution

141 

142client = Client(api_key=os.getenv("XAI_API_KEY"))

143chat = client.chat.create(

144 model="grok-4.7",

145 tools=[

146 web_search(),

147 x_search(),

148 code_execution(),

149 ],

150)

151 

152chat.append(user("What are the latest updates from xAI?"))

153 

154for response, chunk in chat.stream():

155 if chunk.content:

156 print(chunk.content, end="", flush=True)

157 

158print("\nCitations:", response.citations)

159```

160 

161## Citations161## Citations

162 162 

163The API automatically returns source URLs for information gathered via tools. See [Citations](/developers/tools/citations) for details on accessing and using citation data.163The API automatically returns source URLs for information gathered via tools. See [Citations](/developers/tools/citations) for details on accessing and using citation data.

Details

27 27 

28### Basic MCP Tool Usage28### Basic MCP Tool Usage

29 29 

30```pythonXAI

31import os

32 

33from xai_sdk import Client

34from xai_sdk.chat import user

35from xai_sdk.tools import mcp

36 

37client = Client(api_key=os.getenv("XAI_API_KEY"))

38chat = client.chat.create(

39 model="grok-4.7",

40 tools=[

41 mcp(server_url="https://mcp.deepwiki.com/mcp", server_label="deepwiki"),

42 ],

43 include=["verbose_streaming"],

44)

45 

46chat.append(user("What can you do with https://github.com/xai-org/xai-sdk-python?"))

47 

48is_thinking = True

49for response, chunk in chat.stream():

50 # View the server-side tool calls as they are being made in real-time

51 for tool_call in chunk.tool_calls:

52 print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")

53 if response.usage.reasoning_tokens and is_thinking:

54 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

55 if chunk.content and is_thinking:

56 print("\\n\\nFinal Response:")

57 is_thinking = False

58 if chunk.content and not is_thinking:

59 print(chunk.content, end="", flush=True)

60 

61print("\\n\\nUsage:")

62print(response.usage)

63print(response.server_side_tool_usage)

64print("\\n\\nServer Side Tool Calls:")

65print(response.tool_calls)

66```

67 

68```pythonOpenAISDK30```pythonOpenAISDK

69import os31import os

70from openai import OpenAI32from openai import OpenAI


146}'108}'

147```109```

148 110 

111```pythonXAI

112import os

113 

114from xai_sdk import Client

115from xai_sdk.chat import user

116from xai_sdk.tools import mcp

117 

118client = Client(api_key=os.getenv("XAI_API_KEY"))

119chat = client.chat.create(

120 model="grok-4.7",

121 tools=[

122 mcp(server_url="https://mcp.deepwiki.com/mcp", server_label="deepwiki"),

123 ],

124 include=["verbose_streaming"],

125)

126 

127chat.append(user("What can you do with https://github.com/xai-org/xai-sdk-python?"))

128 

129is_thinking = True

130for response, chunk in chat.stream():

131 # View the server-side tool calls as they are being made in real-time

132 for tool_call in chunk.tool_calls:

133 print(f"\\nCalling tool: {tool_call.function.name} with arguments: {tool_call.function.arguments}")

134 if response.usage.reasoning_tokens and is_thinking:

135 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

136 if chunk.content and is_thinking:

137 print("\\n\\nFinal Response:")

138 is_thinking = False

139 if chunk.content and not is_thinking:

140 print(chunk.content, end="", flush=True)

141 

142print("\\n\\nUsage:")

143print(response.usage)

144print(response.server_side_tool_usage)

145print("\\n\\nServer Side Tool Calls:")

146print(response.tool_calls)

147```

148 

149## Tool Enablement and Access Control149## Tool Enablement and Access Control

150 150 

151When you configure a Remote MCP Tool without specifying `allowed_tools`, all tool definitions exposed by the MCP server are automatically injected into the model's context. This means the model gains access to every tool that the MCP server provides, allowing it to use any of them during the conversation.151When you configure a Remote MCP Tool without specifying `allowed_tools`, all tool definitions exposed by the MCP server are automatically injected into the model's context. This means the model gains access to every tool that the MCP server provides, allowing it to use any of them during the conversation.

Details

14 14 

15### Streaming Example15### Streaming Example

16 16 

17```pythonXAI

18import os

19 

20from xai_sdk import Client

21from xai_sdk.chat import user

22from xai_sdk.tools import code_execution, web_search, x_search

23 

24client = Client(api_key=os.getenv("XAI_API_KEY"))

25chat = client.chat.create(

26 model="grok-4.7",

27 tools=[

28 web_search(),

29 x_search(),

30 code_execution(),

31 ],

32 include=["verbose_streaming"],

33)

34 

35chat.append(user("What are the latest updates from xAI?"))

36 

37is_thinking = True

38for response, chunk in chat.stream():

39 # View server-side tool calls in real-time

40 for tool_call in chunk.tool_calls:

41 print(f"\\nCalling tool: {tool_call.function.name}")

42 if response.usage.reasoning_tokens and is_thinking:

43 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

44 if chunk.content and is_thinking:

45 print("\\n\\nFinal Response:")

46 is_thinking = False

47 if chunk.content and not is_thinking:

48 print(chunk.content, end="", flush=True)

49 

50print("\\nCitations:", response.citations)

51```

52 

53```javascriptAISDK17```javascriptAISDK

54import { xai } from '@ai-sdk/xai';18import { xai } from '@ai-sdk/xai';

55import { streamText } from 'ai';19import { streamText } from 'ai';


75}39}

76```40```

77 41 

78## Synchronous Mode

79 

80For simpler use cases or when you want to wait for the complete agentic workflow to finish before processing the response, you can use synchronous requests:

81 

82```pythonXAI42```pythonXAI

83import os43import os

84 44 


94 x_search(),54 x_search(),

95 code_execution(),55 code_execution(),

96 ],56 ],

57 include=["verbose_streaming"],

97)58)

98 59 

99chat.append(user("What is the latest update from xAI?"))60chat.append(user("What are the latest updates from xAI?"))

100 61 

101# Get the final response in one go once it's ready62is_thinking = True

102response = chat.sample()63for response, chunk in chat.stream():

64 # View server-side tool calls in real-time

65 for tool_call in chunk.tool_calls:

66 print(f"\\nCalling tool: {tool_call.function.name}")

67 if response.usage.reasoning_tokens and is_thinking:

68 print(f"\\rThinking... ({response.usage.reasoning_tokens} tokens)", end="", flush=True)

69 if chunk.content and is_thinking:

70 print("\\n\\nFinal Response:")

71 is_thinking = False

72 if chunk.content and not is_thinking:

73 print(chunk.content, end="", flush=True)

103 74 

104print("Final Response:")75print("\\nCitations:", response.citations)

105print(response.content)76```

106 77 

107print("\\nCitations:")78## Synchronous Mode

108print(response.citations)

109 79 

110print("\\nUsage:")80For simpler use cases or when you want to wait for the complete agentic workflow to finish before processing the response, you can use synchronous requests:

111print(response.usage)

112print(response.server_side_tool_usage)

113```

114 81 

115```javascriptAISDK82```javascriptAISDK

116import { xai } from '@ai-sdk/xai';83import { xai } from '@ai-sdk/xai';


134console.log(sources);101console.log(sources);

135```102```

136 103 

137Synchronous requests will wait for the entire agentic process to complete before returning. This is simpler for basic use cases but provides less visibility into intermediate steps.

138 

139## Using Tools with Responses API

140 

141We also support using the Responses API in both streaming and non-streaming modes:

142 

143```pythonXAI104```pythonXAI

144import os105import os

145 106 

146from xai_sdk import Client107from xai_sdk import Client

147from xai_sdk.chat import user108from xai_sdk.chat import user

148from xai_sdk.tools import web_search, x_search109from xai_sdk.tools import code_execution, web_search, x_search

149 110 

150client = Client(api_key=os.getenv("XAI_API_KEY"))111client = Client(api_key=os.getenv("XAI_API_KEY"))

151chat = client.chat.create(112chat = client.chat.create(

152 model="grok-4.7",113 model="grok-4.7",

153 store_messages=True, # Enable Responses API

154 tools=[114 tools=[

155 web_search(),115 web_search(),

156 x_search(),116 x_search(),

117 code_execution(),

157 ],118 ],

158)119)

159 120 

160chat.append(user("What is the latest update from xAI?"))121chat.append(user("What is the latest update from xAI?"))

122 

123# Get the final response in one go once it's ready

161response = chat.sample()124response = chat.sample()

162 125 

126print("Final Response:")

163print(response.content)127print(response.content)

128 

129print("\\nCitations:")

164print(response.citations)130print(response.citations)

165 131 

166# The response id can be used to continue the conversation132print("\\nUsage:")

167print(response.id)133print(response.usage)

134print(response.server_side_tool_usage)

168```135```

169 136 

137Synchronous requests will wait for the entire agentic process to complete before returning. This is simpler for basic use cases but provides less visibility into intermediate steps.

138 

139## Using Tools with Responses API

140 

141We also support using the Responses API in both streaming and non-streaming modes:

142 

170```pythonOpenAISDK143```pythonOpenAISDK

171import os144import os

172from openai import OpenAI145from openai import OpenAI


221}'194}'

222```195```

223 196 

197```pythonXAI

198import os

199 

200from xai_sdk import Client

201from xai_sdk.chat import user

202from xai_sdk.tools import web_search, x_search

203 

204client = Client(api_key=os.getenv("XAI_API_KEY"))

205chat = client.chat.create(

206 model="grok-4.7",

207 store_messages=True, # Enable Responses API

208 tools=[

209 web_search(),

210 x_search(),

211 ],

212)

213 

214chat.append(user("What is the latest update from xAI?"))

215response = chat.sample()

216 

217print(response.content)

218print(response.citations)

219 

220# The response id can be used to continue the conversation

221print(response.id)

222```

223 

224## Accessing Tool Outputs224## Accessing Tool Outputs

225 225 

226By default, server-side tool call outputs are not returned since they can be large. However, you can opt-in to receive them:226By default, server-side tool call outputs are not returned since they can be large. However, you can opt-in to receive them:

Details

134 max_turns=3, # Limit to 3 assistant/tool-call turns134 max_turns=3, # Limit to 3 assistant/tool-call turns

135)135)

136 136 

137chat.append(user("What is the latest news from xAI?"))137chat.append(user("What is the latest news from SpaceXAI?"))

138response = chat.sample()138response = chat.sample()

139print(response.content)139print(response.content)

140```140```


183|-------|-------------|183|-------|-------------|

184| `"function_call"` | Client-side tool - requires local execution |184| `"function_call"` | Client-side tool - requires local execution |

185| `"web_search_call"` | Web-search tool - handled by SpaceXAI server |185| `"web_search_call"` | Web-search tool - handled by SpaceXAI server |

186| `"x_search_call"` | X-search tool - handled by SpaceXAI server |186| `"custom_tool_call"` | X-search tool when `call_id` starts with `xs_` (attachment search uses `as_`) - handled by SpaceXAI server |

187| `"code_interpreter_call"` | Code-execution tool - handled by SpaceXAI server |187| `"code_interpreter_call"` | Code-execution tool - handled by SpaceXAI server |

188| `"file_search_call"` | Collections-search tool - handled by SpaceXAI server |188| `"file_search_call"` | Collections-search tool - handled by SpaceXAI server |

189| `"mcp_call"` | MCP tool - handled by SpaceXAI server |189| `"mcp_call"` | MCP tool - handled by SpaceXAI server |