Skip to main content
Lesson 11 of 12 · Create Videos with Grok

xAI SDK and Automatic Polling

Lesson 11 of 12 Updated November 2, 2026 Intermediate
On this page
  1. Simplify polling with the SDK
  2. A single call instead of a loop
  3. Handle errors at the source
  4. Editing and extension, same ease
  5. Never leave a video in the cloud
  6. Generate multiple videos at once
  7. Should you abandon manual polling?
  8. Key takeaways

Updated November 2, 2026 based on xAI video documentation and xai-sdk 1.21.0 code: methods are called client.video.generate and client.video.extend (editing uses generate with video_url), the result exposes url, the delay is set via a timedelta, and the default polling interval is one second. await requires the asynchronous client AsyncClient, and the VideoGenerationError exception imports from xai_sdk.video. The old version called methods and an exception module that do not exist. Sources: Video Generation · Video Editing · Video Extension · xai-sdk-python.

Simplify polling with the SDK

The previous lesson had you write a polling loop by hand. It’s a useful exercise to understand the generation lifecycle, but it’s also thirty lines of code to maintain in every project, with its own timeout handling, failure cases, and status conversions. xAI’s official Python SDK (pip install xai-sdk) encapsulates all of this: you submit the request, and it handles the wait.

A single call instead of a loop

The client.video.generate method groups submission, tracking, and retrieval. It only returns control once the video is ready, or raises an exception if something went wrong.

import os

import xai_sdk

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

# Text-to-video generation with automatic polling
video = client.video.generate(
    prompt="A sunrise over the Alps, accelerated time-lapse",
    model="grok-imagine-video-1.5",
    duration=5,
    aspect_ratio="16:9",
    resolution="720p",
)

print(video.url)
print(f"Duration: {video.duration} s, model: {video.model}")

Behind these few lines, the SDK does exactly what you coded previously: submitting the request, polling the status every second by default, then returning the result or raising an exception depending on the outcome.

One setting deserves your attention right from deployment: by default, the SDK waits up to 10 minutes before giving up. That’s comfortable for a nightly production script, but far too long for a web request where a user is waiting for a response. The timeout and interval parameters, both timedelta objects, set the limit and the rhythm:

from datetime import timedelta

video = client.video.generate(
    prompt="Your description",
    model="grok-imagine-video-1.5",
    duration=10,
    timeout=timedelta(minutes=5),   # 5 minutes max
    interval=timedelta(seconds=5),  # one check every 5 seconds
)

If you want to keep control over the wait—to relay progress, for example—client.video.start submits the request and returns its request_id, which client.video.get then queries.

Handle errors at the source

The SDK distinguishes failures, which saves you from analyzing strings to figure out what happened:

from xai_sdk.video import VideoGenerationError

try:
    video = client.video.generate(
        prompt="Your description",
        model="grok-imagine-video-1.5",
        duration=5,
    )
    print(f"Video ready: {video.url}")
except VideoGenerationError as e:
    print(f"Generation failed [{e.code}]: {e.message}")
except TimeoutError:
    print("Timeout exceeded: increase the timeout or simplify the request")

Each error calls for a different reaction, and confusing them leads to useless retry loops or, worse, billed generations that yield nothing.

ErrorCauseRecommended action
VideoGenerationErrorFailure signaled by the API: prompt refused, invalid source, service overloaded… (code, message)Read the code: fix the request, or retry later if the service was unavailable
TimeoutErrorClient-side wait time exceededIncrease the timeout or reduce the duration
RuntimeErrorDeferred request expired, or completed without resultRestart the generation
grpc.RpcErrorSDK transport error, including rate limits (code RESOURCE_EXHAUSTED) and key issuesSpace out requests with backoff, or check the key

The SDK uses gRPC: an exceeded rate limit does not appear as an HTTP 429 code here, but as a grpc.RpcError with code RESOURCE_EXHAUSTED.

Editing and extension, same ease

Editing has no dedicated method: it’s generate with a source video, passed as video_url (video up to 8.7 seconds, same duration output, max 720p).

video = client.video.generate(
    prompt="Transform the scene into Japanese anime style",
    model="grok-imagine-video",
    video_url="https://example.com/video.mp4",
)

Extension prolongs a 2 to 15 second sequence by a 2 to 10 second segment, and returns the full video:

video = client.video.extend(
    prompt="Continue the scene with a pan to the right",
    model="grok-imagine-video",
    video_url="https://example.com/video.mp4",
    duration=5,
)

Note the difference with the REST API: the SDK takes the video as a video_url parameter, whereas the JSON body expects a video object with its url property.

Never leave a video in the cloud

The SDK changes nothing about the temporary nature of result URLs. If your code just displays video.url, you paid for a generation that will soon be gone. The best practice is to chain generation and download in the same function, so it becomes impossible to forget the step.

from pathlib import Path

import httpx


async def generate_and_save(client, prompt, output_file, **options):
    """Generates a video and saves it locally (client: xai_sdk.AsyncClient)."""
    video = await client.video.generate(
        prompt=prompt,
        model="grok-imagine-video-1.5",
        **options,
    )

    # Immediate download
    async with httpx.AsyncClient() as http:
        response = await http.get(video.url)
        response.raise_for_status()
        Path(output_file).write_bytes(response.content)

    print(f"Video saved: {output_file}")
    return output_file

For storage without transit through your server, the generation storage_options parameter deposits the video in the xAI Files API, with an expiration of your choice.

Generate multiple videos at once

With the asynchronous client, each call is a coroutine, and asyncio.gather launches an entire batch in parallel. The return_exceptions=True option is decisive here: without it, a single refused request would fail the entire batch and you would lose the results already obtained.

import asyncio
import os

import xai_sdk


async def parallel_generation(requests):
    """Generates multiple videos in parallel."""
    client = xai_sdk.AsyncClient(api_key=os.getenv("XAI_API_KEY"))
    tasks = [
        client.video.generate(
            prompt=req["prompt"],
            model="grok-imagine-video-1.5",
            duration=req.get("duration", 5),
            aspect_ratio=req.get("aspect_ratio", "16:9"),
        )
        for req in requests
    ]

    results = await asyncio.gather(*tasks, return_exceptions=True)

    for i, res in enumerate(results, 1):
        if isinstance(res, Exception):
            print(f"Request {i} failed: {res}")
        else:
            print(f"Request {i} completed: {res.url}")

    return results

Two guardrails frame this parallelization. Submissions consume your rate limit, counted in requests per second according to your team’s tier (10 at the entry tier for video models): for a large batch, bound concurrency with an asyncio.Semaphore. Remember also what parallelism does and does not do: it does not speed up any individual generation, it only reduces the total batch time.

Should you abandon manual polling?

Not systematically. The following comparison shows where each approach takes the lead.

AspectManual pollingSDK
ComplexityHighLow
Error handlingTo implementIntegrated
TimeoutTo implement10 min default, configurable
Polling intervalTo choose1 s default, configurable
FlexibilityTotalStandard (or start and get)
DependencyNonexAI SDK

Manual polling keeps its relevance in two situations: when you need fine control, for example to relay progress to a real-time interface or to integrate it into an existing queue system, and when your deployment context forbids adding an extra dependency.

Key takeaways

  • client.video.generate submits, waits, and returns the video (url, duration, model); 10-minute timeout and one-second check by default
  • Editing uses generate with video_url, extension uses client.video.extend
  • VideoGenerationError (from xai_sdk.video), TimeoutError, and RuntimeError distinguish failures; rate limits arrive as grpc.RpcError
  • Always download the video immediately after generation (temporary URLs)
  • The asynchronous client and asyncio.gather parallelize a batch, within your tier’s rate limit