Skip to main content

What Is an Asynchronous Job?

Every long-running TimeGPT task can be started without waiting for it to finish. NixtlaClient.forecast() blocks until the forecast comes back; its counterpart NixtlaClient.jobs.forecast() sends the same request and returns a Job handle as soon as the server has accepted the work. The handle is how you check on the job later: read job.status to see where it is, call job.wait() to block until it finishes and get the result, or job.cancel() to ask the server to stop. Each jobs method takes the same arguments as the blocking method it mirrors, so moving a call across is usually a one-word change.

Why Use Asynchronous Jobs

  • Keep working while the server does. Submit the job, run other code, and collect the result when you need it.
  • Outlast a single blocking call. Fine-tuning on a long history or simulating thousands of paths can run for a while; a handle lets you decide how long to wait, separately from how long the job may run.
  • Stay in control. Cancel work you no longer need instead of leaving it to consume server-side compute.
  • Run several tasks at once. Submit a batch of independent jobs, then collect them all.
If you only want the result and are happy to wait for it, keep using the blocking methods — they are simpler, and they are the only ones that support num_partitions for splitting a large request. A partitioned call fans out across several jobs and so has no single handle to return.

How to Run a Job

Step 1: Import Packages

Import the required packages and initialize a Nixtla client to connect with TimeGPT.

Step 2: Load Data

This guide uses the classic AirPassengers dataset, a monthly series of international airline passengers from 1949 to 1960.

Step 3: Submit the Job

Call jobs.forecast() with the arguments you would pass to forecast(). It returns as soon as the server accepts the request.
Log output:
The handle carries the job’s identifier and the task it runs:
Validation and preprocessing still happen locally before the request is sent, so an invalid argument or a malformed DataFrame raises immediately rather than producing a job that fails later.

Step 4: Check the Status

job.status asks the server where the job is. It returns a JobStatus, which compares equal to its lowercase string.
A job is pending, then running, and finally reaches one of three terminal states: succeeded, failed or cancelled. Once a job is terminal its status cannot change again, so the handle stops querying the server and answers from what it already knows.

Step 5: Wait for the Result

job.wait() polls until the job reaches a terminal state and returns its result — the same object the blocking method would have given you.

Controlling How Long You Wait

Two independent limits apply to a job, and it helps to keep them apart. How long the client polls is set on wait():
  • poll_interval — seconds between status checks, held fixed. By default the client polls adaptively instead: the first check comes after half a second and the interval doubles up to one check every 15 seconds.
  • poll_timeout — how long to wait for a terminal state before giving up. Defaults to one hour. Pass None to wait until the server reports one.
  • cancel_on_timeout — whether giving up also cancels the job. It defaults to True, so a job you have stopped waiting for stops consuming compute. Set it to False to poll in short increments and call wait() again to resume.
How long the server may spend on the job is set at submission with job_timeout_seconds:
It defaults to your deployment’s own limit for that task, and may not exceed it. Asking for more raises ApiError at submission:
These two do not substitute for one another. poll_timeout only ends the client’s wait; with cancel_on_timeout=False the job keeps running server-side until job_timeout_seconds elapses.

Cancelling a Job

Call cancel() to ask the server to stop work you no longer need:
Cancellation is a request, not an instant stop — the job reaches the cancelled state once the server acts on it, and a wait() still in progress then raises JobCancelledError. Using the handle as a context manager cancels the job for you if anything goes wrong between submitting and collecting the result, including a keyboard interrupt:
A block that exits cleanly cancels nothing, and neither does an exception raised by a job that has already finished.

When a Job Fails

A job that does not succeed raises when you wait on it. Each error names the job so you can tell which one it was. All three are importable from the package root:
A run of failed status checks does not end a wait. The client keeps polling while there is time left on poll_timeout, and reports the last such failure as the cause only if the wait times out.

Running Several Jobs at Once

Because submitting does not block, you can start independent tasks together and collect them afterwards. Here a forecast and a cross-validation run over the same series at the same time:
Both jobs were already running by the time the first wait() was called, so the second result costs only whatever time it still needed.

Available Job Methods

simulate() and explain() always run as jobs underneath — the blocking versions submit one and poll it for you. Use jobs.simulate() or jobs.explain() when you would rather hold the handle yourself.
For the full argument lists of each method, along with Job, JobStatus and the error types, see the SDK reference.