GPT-5.2-Codex: 400K Context, Image Input and Microsoft Foundry

A 400,000-token context window divided into instructions, repository files, the question and image, and room for reasoning and the answer, with three rules for prompt order

GPT-5.2-Codex is OpenAI’s coding model for long, agentic work: large refactors, migrations and feature builds carried across many steps. OpenAI released it in its Codex products on 18 December 2025. On 14 January 2026 it reached the OpenAI API, became generally available through Azure OpenAI in Microsoft Foundry, and became generally available in GitHub Copilot. On 26 January Copilot extended it to Visual Studio, JetBrains IDEs, Xcode and Eclipse.

Three properties get most of the attention: a 400,000-token context window, image input, and availability inside the Azure boundary. This article sets out what OpenAI, Microsoft and GitHub have published about each, with the source beside every figure, and then works through the question an architect has to answer before approving it for a team: how much of a repository fits in that window, what a working session costs, and what the prompt has to look like for the cost to stay down.

ℹ️
SCOPE

Facts about the model come from the vendors’ own pages, linked where they are used. Where a commonly repeated figure has no vendor source, the article says so. The code is a cost model and a client for the Responses API. It was tested with the official openai package against a local stand-in that applies the documented caching rules. No request was sent to the model, and nothing here measures the quality of its answers.

What has been published

PropertyValueSource
Context window400,000 tokensOpenAI model page, Microsoft Foundry announcement
Maximum output128,000 tokensOpenAI model page
Knowledge cutoff31 August 2025OpenAI model page
Input and outputText and images in, text outOpenAI model page
Reasoning effortlow, medium, high, xhighOpenAI model page
APIResponses API; streaming, function calling and structured outputs supported; fine-tuning not supportedOpenAI model page
Price per million tokensInput $1.75, cached input $0.175, output $14.00OpenAI model page, Microsoft Foundry announcement
BenchmarksOpenAI states state-of-the-art results on SWE-Bench Pro and Terminal-Bench 2.0OpenAI announcement
Long sessionsNative context compaction, better tool calling, improved behaviour on WindowsOpenAI announcement
VisionStronger reading of screenshots, technical diagrams, charts and UI surfacesOpenAI announcement
Languages“50+” languages, among them Python, JavaScript and TypeScript, C#, Java, Go and RustMicrosoft Foundry announcement
GitHub CopilotEnterprise, Business, Pro and Pro+; an administrator must enable the model for Business and EnterpriseGitHub changelog, 14 January, 26 January

Some figures that circulate with this model have no source in any of those pages. Neither OpenAI nor Microsoft publishes a HumanEval score for it, a first-token latency, or a tiered list of supported languages, and the benchmark claims on OpenAI’s page are for SWE-Bench Pro and Terminal-Bench 2.0. There is no documented request flag that turns caching on; prompt caching is automatic and depends on how the prompt is built, which is the subject of the second half of this article. If a number matters to a decision, measure it on your own code.

Architecture: three ways to consume the model

The same model is reached through three different products, and they differ in who controls the prompt, where the data goes and how it is paid for.

%%{init: {'theme':'base','themeVariables':{'primaryColor':'#FFF3E0','primaryTextColor':'#232F3E','primaryBorderColor':'#FF9900','lineColor':'#232F3E','clusterBkg':'#F8FAFC','clusterBorder':'#CBD5E1','edgeLabelBackground':'#FFFFFF','nodeTextColor':'#232F3E'}}}%%
flowchart TD
    D["Developer or pipeline"] --> C["Codex CLI, IDE extension or cloud
with a ChatGPT plan"] D --> H["GitHub Copilot
model picker"] D --> A["Your own code or agent
through the Responses API"] A --> O["api.openai.com"] A --> F["Azure OpenAI in
Microsoft Foundry"] C -. "can be pointed at" .-> F style A fill:#FFF3E0,stroke:#FF9900,stroke-width:2px,color:#232F3E style F fill:#EBF8FF,stroke:#0073BB,stroke-width:2px,color:#004C74 style O fill:#F0FFF4,stroke:#1D8102,stroke-width:2px,color:#145202 style H fill:#F3E8FF,stroke:#7C3AED,stroke-width:2px,color:#4C1D95
RouteAdvantagesDisadvantages and risksCost and operations
Codex productsThe harness OpenAI tunes the model for: compaction, sandboxing, tool useThe prompt and context are managed for you. Data handling follows the ChatGPT planPer-seat plan with usage limits
GitHub CopilotAlready in the IDE. Policy is set per organisationContext selection is Copilot’s. No control over prompt layout or cachingPer-seat plan and premium requests
Responses API on api.openai.comFull control of prompt, tools and reasoning effortYou build the harness. Data leaves for OpenAI under your API termsPer token, as in the table above
Responses API through Microsoft FoundrySame control, inside your Azure tenant: Entra ID, private networking, Azure Policy, one billDeployment types and regions differ; check where processing happens for the one you choosePer token at the same list prices

For interactive work the first two routes are the practical ones. The API routes matter when the model is a component: a review bot in a pipeline, a migration tool, an internal agent. That is where prompt layout and cost become the architect’s problem, and the rest of this article is about them.

How much code is 400,000 tokens?

Microsoft’s announcement puts the window at about 100,000 lines of code. That depends heavily on the code. To get a figure from real files, the sample counts a repository of C#, Python and Terraform with tiktoken and the o200k_base encoding, which is the one tiktoken selects for the GPT-5 family.

def count_repository(root: pathlib.Path, suffixes: tuple[str, ...], encoder) -> RepositoryCount:
    """Counts every source file under root with one of the suffixes, in path order, skipping anything that is not UTF-8."""
    files = lines = tokens = 0
    skipped = {"node_modules", "bin", "obj", "out", "site-packages"}
    for path in sorted(p for p in root.rglob("*") if p.is_file() and p.suffix in suffixes):
        if any(part.startswith(".") or part in skipped for part in path.relative_to(root).parts[:-1]):
            continue   # version control, virtual environments, build output and installed packages are not the repository
        try:
            text = path.read_text(encoding="utf-8")
        except UnicodeDecodeError:
            continue
        files += 1
        lines += text.count("\n") + (0 if text.endswith("\n") or not text else 1)
        tokens += len(encoder.encode(text))
    return RepositoryCount(files, lines, tokens)
== 1. How much code is 400,000 tokens?
language      files    lines     tokens  tokens per line  lines in 400,000 tokens
C#              124   10,462    132,069             12.6                   31,686
Python           97    7,374     99,145             13.4                   29,750
Terraform        89    3,013     21,641              7.2                   55,691
all four        310   20,849    252,855             12.1                   32,982

About twelve tokens a line for application code, so 400,000 tokens is nearer 33,000 lines of C# or Python than 100,000. Terraform, with its short lines, is denser. A repository of a few hundred files fits. A typical enterprise service with its tests does not, and the window also has to hold everything else in the request.

One practical point came out of the count. A source file that contains the literal text of a special token, as a tokenizer’s own tests do, makes tiktoken raise an error unless it is told to treat the file as ordinary text. Code is data here, and the counter has to say so.

class PlainText:
    """Wraps a tiktoken encoding so that source code is always counted as ordinary text. Without this, a file
    that happens to contain the characters of a special token, such as a tokenizer test, stops the count."""

    def __init__(self, encoding):
        self._encoding = encoding

    def encode(self, text: str) -> list[int]:
        return self._encoding.encode(text, disallowed_special=())

The window is a budget

A 400,000-token context window divided into instructions, repository files, the question and image, and room for reasoning and the answer, with three rules for prompt order
Everything in a request shares the window: the instructions, the files, the question, and the reasoning and answer that come back.

The window is shared between what is sent and what comes back. Reasoning tokens, which the model spends before it answers, come out of the same window and are billed as output. A request that fills the window with files leaves no room to think. The client therefore checks, before it sends anything, that enough is left.

def room_for_output(prices: Prices, prefix_tokens: int, question_tokens: int) -> int:
    """Tokens left in the window for reasoning and the answer. Negative means the request does not fit."""
    return prices.context_window - prefix_tokens - question_tokens
        left = room_for_output(self._prices, self.prefix_tokens, len(self._encoder.encode(question)))
        if left < self._max_output:
            raise DoesNotFit(f"{self.prefix_tokens:,} tokens of repository leave {left:,} for the answer; {self._max_output:,} are needed.")

The alternative to fitting everything in is the one OpenAI built into its own Codex harness: let the agent read files as it needs them and compact the conversation as it grows. For a tool that asks many questions about a repository small enough to fit, sending the files in full is simpler and, as the next section shows, can be cheap.

Prompt caching is automatic; prompt order is yours

Cached input costs $0.175 per million tokens against $1.75, a tenth. OpenAI and Microsoft document the same rules for when a request is served from the cache.

RuleConsequence
Caching is on by default; there is no flag to setNothing to enable, and nothing to forget
A prompt must be at least 1,024 tokensSmall prompts are never cached
The match is on the prefix: the beginning of the prompt must be identical to an earlier oneAnything that varies has to come after everything that does not
Beyond 1,024 tokens, the cached part is counted in steps of 128The last few tokens of the prefix may be billed at the full rate
An entry is kept for a few minutes of inactivity, and no longer than an hourA burst of questions benefits; one question an hour does not
Caches are not shared between organisations, or between Azure subscriptionsNo cross-tenant exposure, and no benefit from another team’s traffic
The response reports usage.input_tokens_details.cached_tokensYou can, and should, measure the hit rate

The design consequence is a prompt with a fixed order: instructions, then the repository rendered the same way every time, then the question. The session object renders the files once, in path order, and derives a cache key from their content so that requests about the same snapshot are routed to the same cache.

        # Files in path order with a fixed header each: the same repository always renders to the same text.
        self._repository = "\n".join(f"### FILE {path}\n{files[path]}" for path in sorted(files))
        self.prefix_tokens = len(encoder.encode(INSTRUCTIONS)) + len(encoder.encode(self._repository))
        # One key per repository snapshot keeps requests that share this prefix on the same cache.
        self._cache_key = "repo-" + hashlib.sha256(self._repository.encode("utf-8")).hexdigest()[:24]
        response = self._client.responses.create(
            model=self._model,
            instructions=INSTRUCTIONS,
            input=[
                {"role": "user", "content": [{"type": "input_text", "text": self._repository}]},
                {"role": "user", "content": last},
            ],
            reasoning={"effort": self._effort},
            max_output_tokens=self._max_output,
            prompt_cache_key=self._cache_key,
            store=False,   # do not keep the repository in stored responses
        )

The cache key is an OpenAI request parameter. On Microsoft Foundry, check the prompt caching documentation for whether your deployment uses it; the prefix rule applies either way.

The response’s usage block is turned into a cost with the published prices, so every answer comes back with what it cost and how much of it was cached.

    def cost(self, input_tokens: int, cached_tokens: int, output_tokens: int) -> float:
        """Dollars for one request. Output tokens include the reasoning tokens, which are billed as output."""
        fresh = input_tokens - cached_tokens
        return (fresh * self.input + cached_tokens * self.cached_input + output_tokens * self.output) / 1_000_000

To test the client without an account, the sample includes a local server that accepts POST /v1/responses, returns the documented response shape, and applies the rules in the table to the prompts it receives. The official openai package talks to it as it would to the service.

    def _cached(self, key: str, tokens: list[int]) -> int:
        best = 0
        for earlier in self.seen.get(key, []):
            shared = 0
            for a, b in zip(earlier, tokens):
                if a != b:
                    break
                shared += 1
            best = max(best, shared)
        if best < CACHE_MINIMUM:
            return 0
        return best - (best - CACHE_MINIMUM) % CACHE_STEP
== 3. The client against the local stand-in
8 C# files, 23,327 tokens of instructions and repository
ok    the first request is retried after a 429 and then answered
ok    nothing is cached on the first request
ok    a different question about the same repository is served 23,296 cached tokens: the whole prefix, to the nearest step of 128
ok    the second request costs less than a quarter of the first
  first request  23,336 input, 0 cached, $0.0467
  second request 23,337 input, 23,296 cached, $0.0100
ok    the request puts the repository before the question, sets a cache key and does not store the response
ok    an image is sent after the question as a data URL, and the repository is still served from the cache
ok    one changed sentence in front of the repository and nothing is cached
ok    a new snapshot of the repository has its own cache key and starts cold
  refused locally: 390,041 tokens of repository leave 9,955 for the answer; 16,000 are needed.
ok    a repository that leaves no room for the answer is refused before any request is sent
ok    a token provider in place of a key reaches the server as a bearer token, as Entra ID sign-in to Foundry needs

With the layout above, a second question about the same 23,327-token repository is billed for 23,296 cached tokens and costs about a fifth of the first. Put one sentence that changes, such as the date, in front of the files and the cached count is zero. That is the most common way to lose the discount, and it is invisible unless cached_tokens is logged.

⚠️
WHAT THE STAND-IN PROVES

The stand-in is a model of the published rules. The tests show that the client builds requests the way those rules reward and reads the usage fields correctly. They do not show how often the real service returns a cache hit, which also depends on traffic and routing. Log cached_tokens from the first day and treat the cost table below as the best case.

What a session costs

With the prices and the caching rules, the cost of a working session is arithmetic. A session here is one repository and a number of questions, each with a 500-token question and 4,000 tokens of reasoning and answer.

def session_cost(prices: Prices, prefix_tokens: int, question_tokens: int, output_tokens: int, questions: int) -> SessionCost:
    """A session is the same prefix sent with each of several questions. The first request fills the cache;
    the rest are assumed to arrive while it is still warm, which is the best case."""
    each_input = prefix_tokens + question_tokens
    cold = prices.cost(each_input, 0, output_tokens)
    warm = prices.cost(each_input, cacheable(prefix_tokens), output_tokens)
    return SessionCost(questions, cold * questions, cold + warm * (questions - 1))
400,000 input tokens, none cached      $0.70
400,000 input tokens, all cached       $0.07
16,000 output tokens                   $0.22
ok    a full window of input is $0.70, and $0.07 when all of it is cached
ok    cached input is one tenth of the input price
ok    the cacheable part of a prefix is 0 below 1,024 tokens and moves in steps of 128 above it
ok    a 250,000-token repository and a 500-token question leave 149,500 tokens for reasoning and the answer

  repository  questions   no caching   cache warm   saving
      50,000          1        0.14$        0.14$       0%
      50,000         10        1.44$        0.74$      49%
      50,000         40        5.78$        2.71$      53%
     150,000          1        0.32$        0.32$       0%
     150,000         10        3.19$        1.07$      67%
     150,000         40       12.78$        3.57$      72%
     250,000          1        0.49$        0.49$       0%
     250,000         10        4.94$        1.40$      72%
     250,000         40       19.77$        4.42$      78%
Bar chart of the cost of ten questions about repositories of 50,000, 150,000 and 250,000 tokens, with every request cold and with the repository served from the prompt cache
Ten questions about one repository. The saving grows with the size of the repository because the output cost is the same either way.

A full window of input costs $0.70, and $0.07 when all of it is cached. Ten questions about a 250,000-token repository cost $4.94 if every request is cold and $1.40 if the repository is served from the cache after the first. The saving is 72%, not 90%, because output is not discounted and at $14 per million it is a large part of each request. Reasoning effort is therefore the other cost control: a higher setting spends more output tokens on every question.

Image input

The model accepts images, and OpenAI describes better reading of screenshots, diagrams and UI surfaces. In the Responses API an image is a content part beside the text. It belongs with the question, after the repository, so that it does not disturb the cached prefix.

        last: list[dict] = [{"type": "input_text", "text": question}]
        if image is not None:
            kind = mimetypes.guess_type(image.name)[0] or "image/png"
            encoded = base64.b64encode(image.read_bytes()).decode("ascii")
            last.append({"type": "input_image", "image_url": f"data:{kind};base64,{encoded}"})

Images are billed as input tokens, and the count depends on size and detail. Whether the model turns your diagrams into code you would merge is a question about quality, which the vendors’ pages illustrate with examples and do not quantify. Test it on your own design files before building a workflow on it, and treat the output as a draft for review.

Calling it through Microsoft Foundry

Azure OpenAI now has a v1 endpoint that the standard OpenAI client can call directly, with no api-version parameter. The model name in a request is the name of your deployment.

def foundry_client(resource: str, api_key_or_token_provider) -> OpenAI:
    """Azure OpenAI in Microsoft Foundry, v1 endpoint. Pass a key, or a token provider from azure.identity
    (get_bearer_token_provider(DefaultAzureCredential(), "https://ai.azure.com/.default")) to use Entra ID.
    The model argument of the session is then the name of your deployment."""
    return OpenAI(base_url=f"https://{resource}.openai.azure.com/openai/v1/", api_key=api_key_or_token_provider, max_retries=4, timeout=600)

The second argument is either an API key or a token provider. For Entra ID, pass the provider from azure.identity named in the comment; the caller needs the Cognitive Services OpenAI User role on the resource. The test above confirms that the client accepts a provider in place of a key and sends its value as a bearer token. Prefer this to keys: there is no secret to store or rotate, and access is granted and revoked in one place.

The Codex CLI can use the same deployment. Microsoft documents a provider entry in its configuration file:

model = "gpt-5.2-codex"            # the name of your deployment
model_provider = "azure"
model_reasoning_effort = "medium"

[model_providers.azure]
name = "Azure OpenAI"
base_url = "https://YOUR_RESOURCE_NAME.openai.azure.com/openai/v1"
env_key = "AZURE_OPENAI_API_KEY"   # the name of an environment variable, never the key itself
wire_api = "responses"

That file is adapted from Microsoft’s documentation and was not exercised here. Before choosing a deployment type, check in the Foundry model catalogue which regions offer the model and where each type processes data; a global deployment and a data-zone deployment give different answers to a data-residency question.

Security and governance

ConcernWhat to decide
What leaves the boundaryA request with a repository in it sends your source code to the endpoint. Decide which repositories may be sent and to which route. Through Foundry it stays under your Azure agreement and controls
Secrets in the repositoryScan before sending. A key committed years ago is still in the files, and now in a prompt
Stored responsesThe client sets store=False. Confirm your organisation’s data-retention settings with the provider as well
Prompt cacheNot shared across organisations or Azure subscriptions. It is still a copy of your prompt held for minutes; include it in your data-handling description
Instructions hidden in codeA comment or a README in the repository is input to the model. An agent with tools can be steered by it. Give tools the narrowest permissions and review what the agent proposes
Generated codeUntrusted until reviewed, tested and scanned like any other contribution. Keep the same pull-request gates
Security capabilityOpenAI describes this as its strongest model for cybersecurity work so far, with added safeguards and an invite-only programme for vetted defenders. Some security-related requests may be declined
CopilotThe model is off for Business and Enterprise until an administrator enables it. Decide that as policy, not by default
AuditLog who asked, which repository snapshot, token counts including cached tokens, and cost. The cache key in the sample identifies the snapshot
Regulated code and dataIf test fixtures contain personal or clinical data, the prompt does too. Exclude them or use synthetic fixtures

Decision framework

SituationChooseReason
Developers want the model in the editorGitHub Copilot or the Codex productsNo harness to build. Governance is a policy switch
A pipeline step or internal tool, Azure estateResponses API through Foundry with Entra IDTenant controls, no keys, one bill
The same, no Azure requirementResponses API on api.openai.comFewest moving parts
Many questions about a repository under about 250,000 tokensSend the files in full, stable prefix firstSimple, and cached input is a tenth of the price
A repository that does not fit, or long multi-step workAn agent that reads files on demand, as the Codex harness doesThe window is a budget; compaction and retrieval spend it better
Occasional single questionsSend only the relevant filesThe cache will be cold; pay for less input
A decision that depends on answer qualityRun your own evaluation on your own codeThe published benchmarks are OpenAI’s, on other tasks

Key takeaways

  • The published facts are a 400,000-token window, 128,000 tokens of output, text and image input, and $1.75, $0.175 and $14.00 per million tokens for input, cached input and output. OpenAI reports results on SWE-Bench Pro and Terminal-Bench 2.0. No vendor source gives a HumanEval score or a latency figure.
  • Measured on 310 files of C#, Python and Terraform, application code runs at about twelve tokens a line, so the window holds roughly 33,000 lines, less whatever is reserved for reasoning and the answer.
  • Prompt caching needs no flag. It needs a prompt whose beginning does not change: instructions, then the files in a fixed order, then the question and any image.
  • A full window of input is $0.70, or $0.07 cached. Ten questions about a 250,000-token repository are $4.94 cold and $1.40 with a warm cache; output is not discounted, so reasoning effort is the other lever.
  • Through Microsoft Foundry the same API is reached with the standard client and an Entra ID token provider. Decide which repositories may be sent, scan them for secrets, and log cached tokens and cost per request.

If you are logging cached_tokens for this model in production, I would like to hear what hit rate you see and how it varies with traffic. Leave a comment.

📝
HOW THIS WAS CHECKED

The facts in the first table were read from the OpenAI model page and announcement, the Microsoft Foundry announcement and the GitHub changelog, each linked beside the figure. The code ran in GitHub Actions on Python 3.13 with openai 2.16.0 and tiktoken 0.12.0, installed as those packages stood on 1 February 2026. All 16 checks passed: the token count is repeatable, the cost arithmetic matches hand calculation, and the client, driven through the official package against the local stand-in, survives a 429, places the repository before the question, reads cached tokens, keeps an image out of the cached prefix, refuses a request that cannot fit, and sends a token provider’s value as a bearer token. Not run: any request to GPT-5.2-Codex on either endpoint, so nothing here measures answer quality, real cache hit rates, latency or image token counts; the Codex CLI configuration; and Entra ID sign-in against a real tenant. Prices are list prices, not a bill. The run is here.

Glossary

  • Token— The unit a model reads and writes: a word, part of a word or a symbol. Prices and limits are stated in tokens.
  • Context window— The most tokens one request can involve, counting what is sent and what comes back.
  • Reasoning tokens— Tokens the model spends working before it answers. They are not shown, they use the window, and they are billed as output.
  • Reasoning effort— A request setting, from low to xhigh for this model, that controls how much reasoning is spent.
  • Prompt caching— Reuse by the provider of the processed beginning of a prompt that it has seen recently, billed at a lower rate.
  • Prefix— The beginning of a prompt. Caching matches on it, so content that changes must come after content that does not.
  • Responses API— OpenAI’s current API for model requests, also offered by Azure OpenAI through its v1 endpoint.
  • Context compaction— Summarising earlier parts of a long session so that work can continue within the window.
  • Microsoft Foundry— Microsoft’s platform for deploying and governing models in Azure, formerly Azure AI Foundry.
  • Deployment— In Azure OpenAI, a named instance of a model in your resource. Requests use the deployment name as the model.
  • SWE-Bench Pro, Terminal-Bench 2.0— Benchmarks of software-engineering tasks in real repositories and of agent work in a terminal.

References & Further Reading


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.