Build1 publisher2 min readPublished
Codex runs on DeepSeek once you declare what DeepSeek can do
A dev.to walkthrough moves Codex onto DeepSeek's API using two local config files. Codex accepts the capability figures you write into them, and the cost case in the post compares one metered API against another.
The Engineer · Build desk
What happened
- A dev.to walkthrough puts Codex on DeepSeek by adding a model_provider block to ~/.codex/config.toml and pointing model_catalog_json at a hand-written ~/.codex/models.json.
- The provider block sets base_url to https://api.deepseek.com, reads credentials from the DEEPSEEK_API_KEY environment variable, and declares wire_api as "responses".
- The catalog file declares what the model can do: text and image input, parallel tool calls, a search tool, and a context window of 1,048,576 tokens.
- Verification in the post is visual: after a restart, DeepSeek-Flash shows in the bottom right corner of Codex as the active default model.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint The nine-to-one gap rests on off-peak rates, so a team whose agent loops run during business hours has to reprice before quoting it.
- decision Whoever writes models.json is making capability claims on the provider's behalf. A failed tool call then lands on the person who typed the file, not on a support queue.
- cost Metered billing takes the plan cap out of the picture, so every retry inside an unattended agent loop bills at the provider's rate.
The catalog file is the half that carries risk. The author's framing is that it is authoritative: the setup "fully maps the model, so Codex knows it supports parallel tool calls, large context windows, and specific reasoning levels" [15]. Nothing in the procedure asks the endpoint to confirm any of that. The context window figure and the parallel-tool-call flag are values typed into a local JSON file [4]. Its description field says "Latest frontier agentic coding model with image input" [7], a product review of someone else's endpoint written by the person paying for it.
For those declarations to hold, api.deepseek.com has to accept requests in the shape that `wire_api = "responses"` names [3], and it has to recognise the slug the model line points at [2]. The pricing section of the post prices a model it calls DeepSeek-V4-Flash [12] while the catalog slug is `deepseek-flash` [2]. A label in the corner of the interface after a restart [8] shows Codex parsed the file. The first tool call is where a mismatch would surface.
Divide the post's dollar figures by its token counts and the unit prices fall out: $1.26 per million input tokens and $10.00 per million output for GPT-5, against $0.22 and $0.70 for the DeepSeek model [1]. The weekly totals differ by a factor of about nine [2]. Two conditions sit under that. The DeepSeek column is off-peak pricing [12]. And the example week's 500,000 input tokens [10] comes to under half of a single maximum-context request at the window the catalog declares [3].
The second condition is the one I would check first. An agent that resends repository context on every turn spends input tokens in proportion to turns, so the post's week is a handful of full-context turns [3]. Its promise of "large refactoring tasks without worrying about subscription limits or high API bills" [13] is priced on that week.
The stated reason for the switch is the subscription: you "only need to top up your DeepSeek API balance and pay for exactly what you use" [14]. The comparison the post publishes is GPT-5's API against DeepSeek's API. It does not give the price of the plan being dropped. At $1.63 a week, the GPT-5 side annualises to about $85 [4].
Inside the catalog, `truncation_policy` is set to mode `tokens` with a limit of 10000 [6]. The default reasoning effort ships as `high`, the middle of the three the file declares, with `low` described as "Fast responses with lighter reasoning" [5].
What to watch
- A published multi-turn session transcript of Codex driving DeepSeek through tool calls would test whether the wire_api = "responses" setting actually holds.