Skip to content

Pinning a Dated Grok Model Snapshot

9 min read · updated August 11, 2026

grok-latest is a subscription to xAI’s release schedule. If a tuned prompt sits behind it, you want a dated snapshot and a check that you actually got one.

The three name forms

xAI documents the scheme on its model list. Some models carry aliases so that users migrate automatically to the next version, and the forms are:

  • <modelname> — aliased to the latest stable version.
  • <modelname>-latest — aliased to the latest version, documented as suitable for users who want the newest features.
  • <modelname>-<date> — a specific model release, documented as one that will not be updated and is for workflows demanding consistency.

xAI recommends the aliased forms for most users, which is honest advice for exploratory work and the wrong advice for a production prompt whose output feeds a parser. The dated form is the one this page is about. Current examples on the model list include grok-4.20-0309-reasoning, grok-4.20-0309-non-reasoning and grok-4.20-multi-agent-0309 — a family name, a date, and where relevant a mode.

Model slugs on this page are examples from xAI’s model list at the time of writing. Read the current list rather than copying a slug out of any article, including this one — including because eight slugs were retired on 15 May 2026.

Making the swap

  1. Find out what you are actually calling. Search your repository for the alias, including in configuration and infrastructure, not only in application code.
    rg -n "grok-latest|grok-4\.5-latest|grok-build-latest" \
       --glob '!node_modules'
  2. List the models the key can reach. The models endpoint is OpenAI-shaped and reflects your account and region rather than the documentation.
    curl -s https://api.x.ai/v1/models \
      -H "Authorization: Bearer $XAI_API_KEY" | jq -r '.data[].id' | sort
  3. Record which model the alias resolves to today. This is the version you are already validated against, and it is the pin you want. Ask for the alias and read what answered.
    curl -s https://api.x.ai/v1/chat/completions \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $XAI_API_KEY" \
      -d '{"model":"grok-latest",
           "messages":[{"role":"user","content":"ping"}],
           "max_completion_tokens":5}' | jq -r '.model'
  4. Put the dated slug in exactly one place. A constant or an environment variable. The cost of the next migration is proportional to the number of call sites holding a literal.
    # .env
    XAI_MODEL=grok-4.20-0309-reasoning
    GROK_MODEL = os.environ["XAI_MODEL"]   # no default; fail loudly if unset
  5. Send one request against the pin and read the whole response. Confirm it answers, confirm the model field, and confirm the usage object looks like what you expect.
    curl -s https://api.x.ai/v1/chat/completions \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $XAI_API_KEY" \
      -d "{\"model\":\"$XAI_MODEL\",
           \"messages\":[{\"role\":\"user\",\"content\":\"Reply with the single word: ok\"}],
           \"max_completion_tokens\":10}" \
      | jq '{requested: env.XAI_MODEL, served: .model, usage}'
  6. Re-check the parameters the pin may have changed. A dated snapshot can differ from the alias in context window, in whether it reasons, and therefore in which parameters it accepts — stop is rejected by reasoning models, and a non-reasoning snapshot will ignore a reasoning_effort you were relying on.
  7. Run your evaluation set once against the pin. Twenty saved prompts with expected output shapes is enough to catch a format contract breaking. Keep the results; they are the baseline you will compare the next model against.

Verifying the pin held

A pin you do not check is a comment. Every response carries a model field naming what actually served the request, and comparing it to what you asked for is the entire verification.

import os, logging
from openai import OpenAI

client = OpenAI(api_key=os.environ["XAI_API_KEY"],
                base_url="https://api.x.ai/v1")
MODEL = os.environ["XAI_MODEL"]

def complete(messages, **kwargs):
    r = client.chat.completions.create(
        model=MODEL, messages=messages,
        max_completion_tokens=kwargs.pop("max_completion_tokens", 1024),
        **kwargs)
    if r.model != MODEL:
        logging.warning("model substituted: requested=%s served=%s", MODEL, r.model)
    return r

That warning is the only thing standing between you and a silent substitution. xAI’s documented retirement behaviour is to redirect retired slugs to a current model rather than to fail them, so the absence of an error is not evidence that your pin is being honoured.

What a pin does not freeze

A dated slug fixes the weights. It does not fix everything that determines what comes back, and being clear about the difference is what stops a pin being mistaken for reproducibility.

  • Sampling. The same pinned model with the same prompt still samples from a distribution. seed is documented as being for deterministic sampling with the caveat that repeated requests should yield similar results — similar, not identical. If you need byte-identical output, the answer is a cache, not a pin.
  • Server-side tools. If your request enables web or X search, the retrieved content changes daily by design. The model is pinned; what it reads is not, and it is usually the larger influence on the answer.
  • The injected safety prefix. xAI publishes prefixes attached to API model slugs, as described in the default system prompt page. Those are on xAI’s side of the boundary and can be revised without a new model id.
  • Serving infrastructure. Region availability differs by model and can change, and latency, batching behaviour and cache hit rates are properties of the service rather than of the checkpoint.
  • Price. Nothing about a dated slug fixes what it costs. A pin protects your output contract, not your unit economics.

None of that argues against pinning. It argues for knowing what you bought: a pin removes one large source of unannounced change and leaves the others in place, which is exactly why the evaluation set in step seven is not optional.

A pin is not permanent

The documentation says a dated release will not be updated. It does not say it will exist forever, and the May 2026 retirement included grok-4-0709 — a dated snapshot. Pinning buys you stability between migrations, not exemption from them.

So pair the pin with two habits. Watch xAI’s developer documentation for retirement notices, since that is where the dates are published. And keep the substitution warning above wired to something that actually pages someone, because on the day a pin expires that log line is your only notification.

It is also worth writing down, next to the pinned slug, what you pinned it for. Six months later the constant is just a string, and the question at migration time is not “is there a newer model” — there always is — but “what did this version do that we depended on”. A one-line comment naming the reason (a format contract, a tuned prompt, an audit requirement) turns the next migration from an archaeology exercise into a decision.

When not to pin

Pinning has a real cost: you stop receiving improvements, and you take on the work of migrating deliberately. It is worth it when output shape is load-bearing — a parser downstream, a schema contract, a user-visible format — or when a prompt has been tuned against a specific model’s quirks, or when a regulated process requires you to state which model produced an output.

It is not worth it for exploratory work, for internal tools where a changed answer is merely noticed, or for anything you would rather have improve on its own. In those cases xAI’s recommendation stands and the alias is the right choice. The failure to avoid is the middle case: a production dependency on an alias that nobody decided on. The same trade-off exists at every provider — the OpenAI snapshot scheme and Anthropic’s dated model ids answer the same question with different naming.