Off-Peak AI Agent Scheduling: Route Flexible Work by the Clock, Not by Vibe
Off-peak AI agent scheduling by the clock: classify jobs, map DeepSeek's UTC peak hours and Anthropic's cache timers, route with caps, and review weekly.
Go deeper. Build your own.
It is 09:30 UTC on a Tuesday, and a research agent that nobody will read until Thursday is paying DeepSeek’s peak rate because someone pressed Enter when the idea occurred to them. Thirty minutes later the same job would have cost half. Nothing about the job changed at 10:00; only the price did, and the fleet had no way to know.
Off-peak AI agent scheduling is the fix, and it is smaller than it sounds: a job classifier, a calendar of provider windows, a routing table with caps, and a weekly review of what moved. By next Tuesday every recurring job in your fleet carries a class, latency-bound or flexible; every flexible job knows the cheapest window it may wait for and the longest it may wait; every lane has a ceiling; and Friday’s review shows what ran where, what it cost, and what waited too long.
Two pieces of September pricing weather make the schedule worth writing now, and they pull in different directions. DeepSeek’s discount is a clock. Anthropic’s is a cache with a timer. A router that only understands one of them will route the other one wrong.
Two discounts, two dialects: a clock and a cache timer
On September 10, 2026, DeepSeek launched V4.1-Flash with a price cut and a repeat of its standing rule, stated in the launch post as “Off-peak rates are 50% of peak rates. Schedule flexible workloads off-peak to save.” The window is defined on the pricing page by its peak hours, 01:00 to 04:00 and 06:00 to 10:00 UTC, Monday through Friday, excluding Chinese public holidays. Then: “All other hours are off-peak, including weekends and Chinese public holidays in full.”
The deepseek-flash model bills a cache hit at $0.006 per million tokens at peak and $0.003 off-peak, a cache miss at $0.30 and $0.15, and output at $1.20 and $0.60.
Screenshot: DeepSeek API Docs, “Models & Pricing” (page undated), captured Sep 19, 2026.
Nine days earlier, on September 1, 2026, Anthropic released Claude Fable 5.1 with no clock at all. The launch page says “Cache reads now cost 75% less, or $0.25 per million tokens.” and claims that “For typical workloads, costs are reduced by around 25% relative to Fable 5. For complex coding and highly agentic tasks, the savings could be up to around 45%.”
Those are Anthropic’s numbers, and the chart they sit under carries the method: “Indexed cost of running the same workloads on Fable 5 and Fable 5.1, at usage-based pricing measured at default effort over four weeks of actual usage in August 2026.” The pricing docs put the multiplier at 0.025× the $10 base input price, against 0.1× on every other Claude model.
Screenshot: Anthropic, “Introducing Claude Fable 5.1 and Claude Mythos 5.1” (September 2026), captured Sep 19, 2026.
The timer is in the prompt caching docs: “By default, the cache has a 5-minute lifetime. The cache is refreshed for no additional cost each time the cached content is used.” A one-hour lifetime costs 2× base input to write instead of 1.25×, and the pricing docs say “caching pays off after one cache read for the 5-minute duration (1.25x write), or after two cache reads for the 1-hour duration (2x write)”.
So Anthropic’s window is not an hour of the day. It is the gap between two calls that share a prefix.
There is a third dialect, and it is neither a clock nor a timer. OpenAI’s GPT-6 Astra model page says “Batch and Flex are priced at 50% of Standard rates.” and Anthropic’s batch pricing puts Fable 5.1 at $5 input and $25 output, half of standard. A queue with a discount: you give up the right to say when, and the provider gives up half the price. Three discounts, and each one asks you to promise something different.
Why off-peak AI agent scheduling needs a router, not a reminder
Chatbots suggest; agents act, and the ones that act overnight, in fan-outs, on recurring schedules, are the ones whose start time nobody is watching. A reminder in a channel to run the big jobs after 10:00 UTC lasts until the first person in a different time zone joins the team. A router that reads the job’s class and the provider’s window, and refuses to release a flexible job into a peak block, lasts as long as the config does.
The sibling pieces cover the two providers in depth: the DeepSeek off-peak and cache-hit runbook for prefix shape and re-based budgets, and metering overnight Claude jobs on cheaper cache reads for the dollars-per-job ledger. This piece is the schedule that sits above both.
Step 1: Classify every recurring job as latency-bound or flexible, in writing
A job is latency-bound if a person or a pipeline is waiting on its output, or if its inputs go stale faster than the wait. Everything else is flexible, and flexible is the default: a job earns latency-bound status by naming who is waiting.
| Job (examples) | Class | Max delay | Cache shape | Runs multi-turn | Default lane |
|---|---|---|---|---|---|
| PR review on open MRs | latency-bound | 15 min | stable prefix per repo | yes, short | nearest lane, any window |
| CI failure triage | latency-bound | 10 min | small prefix | yes, short | nearest lane, any window |
| Nightly literature or competitor research | flexible | 14 h | stable prefix, long tail | yes, long | DeepSeek off-peak |
| Eval reruns after a model change | flexible | 24 h | stable prefix per suite | yes | DeepSeek off-peak, or batch |
| Doc sweeps, changelog drafts | flexible | 24 h | stable prefix per repo | yes | Anthropic, 1-hour cache, no clock |
| Bulk classification, one-shot summaries | flexible | 48 h | none reused | no | batch lane, 50% |
| Fan-out research (many workers) | flexible | 14 h | stable prefix per worker | yes | DeepSeek off-peak, capped |
Write the max delay as a number, not a mood. It is the field the router uses to decide whether a job can wait for the next off-peak edge, and the field the weekly review uses to catch jobs that waited longer than they were allowed to.
Step 2: Map every provider window onto one UTC calendar
One calendar, one time zone, and it is UTC: DeepSeek writes its window in UTC, and UTC never shifts for daylight saving.
Seven peak hours per weekday, split by a two-hour trough. The other seventeen, plus every weekend hour, are half price. Source: DeepSeek API pricing page; pricing effective Sep 10, 2026.
The DeepSeek window in local clocks for September, for the people who will read the schedule:
| Peak block | UTC | Beijing (UTC+8) | Central Europe, CEST (UTC+2) | US Pacific, PDT (UTC−7) |
|---|---|---|---|---|
| Morning peak | 01:00–04:00 | 09:00–12:00 | 03:00–06:00 | 18:00–21:00 previous day |
| Afternoon peak | 06:00–10:00 | 14:00–18:00 | 08:00–12:00 | 23:00 previous day–03:00 |
| Off-peak stretch | 10:00–01:00 | 18:00–09:00 | 12:00–03:00 | 03:00–18:00 |
The blocks line up with Beijing’s working day, midday break included, and the whole weekend is off-peak. Two things the docs do not give you: the list of Chinese public holidays, and any promise that the window stays put. Keep the holiday list as a file in the repo, re-check the pricing page each quarter, and recompute the local columns when Europe and the US leave summer time on October 25 and November 1.
Anthropic’s row on the same calendar is a rule rather than a block: a job that reuses a prefix must either keep its calls within five minutes of each other or pay for the one-hour cache. The docs’ own advice is to “send a new pre-warm request at least every 5 minutes to keep the cache warm. For longer gaps between user requests, use the 1-hour cache duration instead.”
A tool call that runs a twelve-minute test suite between two model calls silently breaks the five-minute cache, and the next call writes the prefix again at 1.25×. That is the Anthropic equivalent of a peak hour: an idle gap.
The batch row has no window either, only a promise you make about not needing the answer soon. It belongs on the calendar as a lane, not a time.
# provider-windows.yaml (illustrative shape; one file, read by the router only)
deepseek:
kind: clock
tz: UTC
peak_blocks: {days: [Mon, Tue, Wed, Thu, Fri], hours: ["01:00-04:00", "06:00-10:00"]}
holidays_file: calendars/cn-public-holidays-2026.yaml # off-peak in full
off_peak_multiplier: 0.5
model: deepseek-flash
anthropic:
kind: cache_timer
model: claude-fable-5-1
cache_ttl_default: 5m # refreshed at no cost on each use
cache_ttl_long: 1h # 2x write instead of 1.25x; use when call gaps exceed 5m
min_cacheable_tokens: 512
batch:
kind: queue
discount: 0.5 # OpenAI Batch/Flex; Anthropic Batch API
deadline_hint: "the provider sets the turnaround, not you; treat as flexible with max delay >= 24h"
Step 3: Write the routing table, and put a cap on every row
The router reads the job’s class, max delay and cache shape, and picks a lane and a release time. Every row carries a cap, because a scheduler that knows when to run a job but not how many to run at once has only learned half the trick; the arithmetic for the concurrency half is in metering subagent fan-out.
| Class | Cache shape | Lane | Release rule | Caps (illustrative) |
|---|---|---|---|---|
| Latency-bound | any | nearest healthy lane | now | $2 per job; 8 concurrent |
| Flexible, multi-turn, stable prefix | prefix reused across turns | DeepSeek off-peak | next off-peak edge, wait ≤ max delay | $1 per job; $40 per night; 12 concurrent |
| Flexible, multi-turn, slow tools | prefix reused, call gaps > 5 min | Anthropic, 1-hour cache | now, no clock | $6 per job; $60 per night; 4 concurrent |
| Flexible, one-shot bulk | no prefix reuse | batch lane (50%) | enqueue immediately | $25 per batch; 1 batch in flight per suite |
| Flexible fan-out | prefix reused per worker | DeepSeek off-peak, capped | 10:00 UTC weekday or 00:00 UTC Saturday | 16 workers; $80 per fan-out |
| Any, provider degraded | any | next lane in the failover matrix | now | same caps, alert on switch |
The last row is the one people forget. A job routed off-peak to a lane that is degraded at 10:00 UTC either waits (fine, if within max delay) or fails over (fine, if the target is a real lane). Which of the two happens should be a rule in the table, and forced routing inside a provider’s catalog is the case where the lane changes under you without failing at all.
# routing.yaml (illustrative shape)
rules:
- match: {class: latency_bound}
lane: nearest_healthy
release: now
caps: {usd_per_job: 2, concurrent: 8}
- match: {class: flexible, cache_shape: stable_prefix, slow_tools: false}
lane: deepseek
release: {at: next_off_peak_edge, max_wait_hours: from_job}
caps: {usd_per_job: 1, usd_per_night: 40, concurrent: 12}
- match: {class: flexible, cache_shape: stable_prefix, slow_tools: true}
lane: anthropic
cache_ttl: 1h
release: now
caps: {usd_per_job: 6, usd_per_night: 60, concurrent: 4}
- match: {class: flexible, cache_shape: none}
lane: batch
release: enqueue
caps: {usd_per_batch: 25, in_flight: 1}
on_wait_exceeded: fail_and_page # never silently run at peak
The on_wait_exceeded line is the policy in one word. A flexible job that cannot make its window is a broken job, and the honest outcome is a page, not a quiet run at double the price.
Step 4: The weekday off-peak AI agent scheduling table, hour by hour in UTC
Here is what the router produces for a weekday. Picture a fleet with nightly research, a few eval suites, doc sweeps with slow test tools, and a PR-review lane that never sleeps.
| UTC | DeepSeek lane | Anthropic lane | Batch lane | Notes |
|---|---|---|---|---|
| 00:00–01:00 | off-peak, last hour before the 01:00 block: release only jobs that finish by 01:00 | doc sweeps run, 1-hour cache | drain results | a long job started here runs into peak; hold it for 10:00 |
| 01:00–04:00 | peak: hold flexible; latency-bound only | keep running; gaps under 5 min or 1-hour cache | enqueue new bulk | held jobs release at 04:00 if they fit the trough, else at 10:00 |
| 04:00–06:00 | off-peak trough: release jobs that finish inside 2 h | same | same | a fan-out that needs 3 h waits for 10:00 instead |
| 06:00–10:00 | peak: hold flexible | same | same | PR review keeps running here; that is what latency-bound means |
| 10:00–24:00 | off-peak, 14 h: nightly research, eval reruns, fan-outs | same | drain | the main flexible window; concurrency caps do the rest |
| Sat 00:00 onward | off-peak for 48 h | same | same | weekly backfills and the largest fan-outs start here |
Job class → window → provider → cap → run → review. Latency-bound jobs skip the window step; nothing skips the cap.
Two details make the timetable work. The 04:00 trough and the 00:00 hour are real money for short jobs and a trap for long ones, so the router releases into them only what fits; the rule is release-if-duration-fits, using the job’s trailing median runtime. And the weekend is the plan: a fan-out that costs $80 on a weekday night costs the same $80 on Saturday, but from Saturday 00:00 UTC the next peak block is 49 hours away, so the weekly heavy jobs are pinned there by default.
Step 5: Keep the caches warm on the lanes that reward it
On the Anthropic lane the clock is the gap between calls, so the scheduler’s job is to keep chained calls dense or to buy the hour. Minimum cacheable prompt on Fable 5.1 is 512 tokens; below that there is nothing to keep warm. Read cache_creation_input_tokens and cache_read_input_tokens from every response: the docs say that when both are zero, the prompt was not cached, which is the signal that a prefix fell under the minimum or that a breakpoint moved.
On the DeepSeek lane the context cache is best-effort and clears after hours to days of idleness, so a flexible job held from 06:00 to 10:00 may pay one miss on its first request after release. That is one 50k-token miss at $0.15 off-peak, about a cent, against a job that would otherwise have run at double price. Hold anyway. The prefix rules that keep turn two onward hitting are in the DeepSeek sibling and do not change with the clock.
The one place the two lanes interact is a job that starts on one and fails over to the other mid-run. The prefix that hit on DeepSeek is a fresh write on Anthropic, and the Anthropic cache that was warm is worthless to DeepSeek. Failover between lanes resets the cache; budget the reset and do not let a job flap between lanes because one of them was slow for a minute.
Step 6: The Friday review of what moved, and what waited too long
The review is one page, generated, and it answers five questions.
| Question | Metric | Healthy | Act when |
|---|---|---|---|
| Did flexible work actually run off-peak? | share of flexible DeepSeek tokens billed at off-peak | ≥ 90% | < 75%: the router is releasing into peak, or class labels are wrong |
| Did anything wait past its max delay? | count of wait_exceeded pages |
0 | any: raise max delay, or reclass the job, never both |
| Are caches hitting? | hit rate per lane, turn 3 onward | DeepSeek ≥ 80%; Anthropic reads ≥ 70% of input | < 50%: prefix churn or a slow-tool gap; fix the job, not the lane |
| Did a lane change price under us? | lane spend vs trailing-week median, volume-adjusted | within ±30% | outside: a routing or pricing change; check the provider’s page before touching config |
| What did a job cost, per lane? | USD per job, per class, per lane | trending flat or down | a class that got dearer on the lane the table assigned it: revisit the row |
The fourth row is where cost-anomaly alerts plug in; a drop is as much an anomaly as a spike, and after September 14 a deepseek-v4-pro line suddenly billing at Flash rates was exactly that signal. The fifth row is what makes the routing table a living document: a lane that stops being the cheapest for a class loses the row.
# illustrative weekly rollup from the job ledger (one JSON line per job)
jq -r 'select(.class=="flexible") | [.lane, .window, .usd, (.wait_h > .max_delay_h)] | @tsv' jobs.jsonl \
| awk '{n[$1" "$2]++; usd[$1" "$2]+=$3; late+=($4=="true")}
END {for (k in n) printf "%-22s jobs=%d usd=%.2f\n", k, n[k], usd[k]; print "late:", late}'
Keep the review in the same place the fleet’s session evidence lives, so that a job that cost three times its class median can be replayed rather than argued about.
Six ways an off-peak schedule drifts, and the signal for each
The window is written in local time. Someone set the peak blocks as 03:00–06:00 because that is what their laptop showed. Signal: the review’s off-peak share falls under 75% with no config change you remember. Fix: the window file has one tz: UTC line and a test that asserts it.
A holiday is missed, or invented. The router holds jobs on a day that is off-peak in full, or releases into peak on a day it thought was a holiday. Signal: a held queue on a Chinese public holiday, or a peak-billed weekday you expected to be free. Fix: the holiday file carries a source and a checked-on date, because DeepSeek’s pricing page excludes Chinese public holidays without listing them.
A flexible job is secretly latency-bound. It waited fourteen hours and the person who wanted it on Wednesday morning got it Wednesday night. Signal: a complaint, which is the worst signal. Fix: max delay is a required field at job creation, and the person who sets it is the person who waits.
The Anthropic cache goes cold mid-job. A tool call ran eleven minutes; the next model call wrote the whole prefix again. Signal: cache_creation_input_tokens jumps mid-job on a lane using the 5-minute default. Fix: any job whose tools can exceed five minutes is routed with the 1-hour cache, per the table.
A batch never comes back in time. The job was classified as bulk and enqueued, and the deadline was shorter than the provider’s turnaround. Signal: wait_exceeded on a batch lane. Fix: batch is only for max delay of 24 hours or more, and the table says so.
The lane changed price under you. A provider rerouted a model id, a promotion ended, a peak window moved. Signal: the fourth review row, or the anomaly floor. Fix: the pricing pages are the source of truth, checked on a date you record; the routing table changes only after that check.
The operating layer holds the clock
No model ships the router, the calendar, the caps or the review. They belong to the layer that starts, holds, steers and stops agents across every provider and CLI you run, which is the command-center thesis applied to the bill. A model’s price list is a fact about a vendor. Which jobs may wait for the cheap hours is a decision about your fleet, made once, in one file, and inherited by every agent that runs.
That is also the difference between the operating bill and the token bill: the token bill is what the providers charge, and the operating bill is what it costs you to be the one who decides when. This schedule moves the second one from a person’s memory to a config that the overnight merge gates already assume exists.
FAQ: off-peak AI agent scheduling
When is DeepSeek off-peak in UTC?
DeepSeek’s peak hours are 01:00 to 04:00 and 06:00 to 10:00 UTC, Monday through Friday, excluding Chinese public holidays. Every other hour is off-peak at half the peak rate, including the weekday trough from 04:00 to 06:00, all weekend hours, and Chinese public holidays in full, per the API pricing page.
Does Anthropic have off-peak pricing for Claude?
No clock-based discount is published. Claude Fable 5.1 cuts the cache-read price to $0.25 per million tokens, 0.025× base input, and the Batch API halves input and output prices. The saving depends on prefix reuse and on keeping call gaps under the 5-minute cache lifetime, or paying for the 1-hour cache.
How do I decide which agent jobs can run off-peak?
Classify every recurring job as latency-bound or flexible, and require a maximum delay in hours on each. A job is latency-bound only when a named person or pipeline waits on it. Flexible jobs with a stable prompt prefix go to a clock-priced lane off-peak; one-shot bulk goes to a batch lane; everything carries a cap.
Sources
- DeepSeek API Docs, Models & Pricing
- DeepSeek, Introducing DeepSeek-V4.1-Flash: smarter, faster, more efficient. (September 10, 2026)
- DeepSeek API Docs, Context Caching
- @deepseek_ai on X, V4.1-Flash launch thread, pricing post (September 10, 2026)
- Anthropic, Introducing Claude Fable 5.1 and Claude Mythos 5.1 (September 2026)
- Anthropic, Pricing
- Anthropic, Prompt caching
- OpenAI, GPT-6 Astra model page (Batch and Flex at 50% of Standard rates)
