For AI agents: markdown of this page — /docs-content-en/ai/consumption/off-peak.md documentation index — /llms.txt
Off-peak schedule
GET /v1/off-peak
Returns the off-peak schedule: how much more slowly the company AI quota is consumed during each hour of the week and when the next discounted window arrives.
The discount applies to quota consumption — during off-peak hours the same call takes a smaller share of the monthly limit. Tokens paid from the wallet are not discounted. The endpoint is meant for planning: an agent reads the schedule and moves deferrable bulk tasks into the cheap windows.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
model (query) |
string | no | — | Model identifier from GET /v1/models. Returns the schedule for that model. Without this parameter, returns the schedule the platform marked as the main one |
Examples
curl — personal key
curl "https://vibecode.bitrix24.com/v1/off-peak?model=bitrix/bitrixgpt-5.5" \
-H "X-Api-Key: YOUR_API_KEY"
curl — OAuth application
curl "https://vibecode.bitrix24.com/v1/off-peak?model=bitrix/bitrixgpt-5.5" \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript — personal key
const url = 'https://vibecode.bitrix24.com/v1/off-peak?model=bitrix/bitrixgpt-5.5'
const res = await fetch(url, {
headers: { 'X-Api-Key': 'YOUR_API_KEY' },
})
const schedule = await res.json()
if (schedule.enabled && schedule.currentMultiplier < 1) {
const discount = Math.round((1 - schedule.currentMultiplier) * 100)
console.log(`Off-peak hour now: quota is consumed ${discount}% more slowly`)
} else if (schedule.nextWindow) {
console.log(`Cheaper in ${schedule.nextWindow.inHours} h`)
}
JavaScript — OAuth application
const url = 'https://vibecode.bitrix24.com/v1/off-peak?model=bitrix/bitrixgpt-5.5'
const res = await fetch(url, {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const schedule = await res.json()
Response fields
The successful response is the schedule object itself, without a success or data wrapper.
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Whether a schedule is in effect for the requested model. When false, every hour is at full price and the remaining fields are empty |
timezone |
string | null | Schedule timezone — an identifier from the IANA time zone database, for example UTC. The day of week and hour are computed against it |
currentMultiplier |
number | Quota-consumption multiplier for the current hour, greater than 0 and no more than 1. A value of 0.5 means the quota is consumed twice as slowly, a value of 1 means no discount |
nextWindow |
object | null | The nearest hour strictly cheaper than the current one, within the next week. null when there is no such hour |
nextWindow.inHours |
number | How many hours until that hour arrives |
nextWindow.multiplier |
number | Quota-consumption multiplier in that hour |
currentWindowEndsInHours |
number | null | How many hours until consumption stops being this favorable. null when it never gets more expensive within the next week — including when the current hour is at full price |
grid |
array | null | Grid of multipliers grid[day][hour]. Seven rows of 24 values. Day 0 is Sunday, day 6 is Saturday. The hour is 0 to 23 in the timezone time zone |
nowCell |
object | null | The grid cell that the current moment maps to. Computed on the server, so it matches currentMultiplier |
nowCell.dow |
number | Day of week, from 0 (Sunday) to 6 (Saturday) |
nowCell.hour |
number | Hour, from 0 to 23 |
Response example
The schedule is in effect:
{
"enabled": true,
"timezone": "UTC",
"currentMultiplier": 1,
"nextWindow": { "inHours": 2, "multiplier": 0.92 },
"currentWindowEndsInHours": null,
"grid": [
[0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.55, 0.55, 0.55, 0.6, 0.6, 0.6, 0.6, 0.55, 0.55, 0.55, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5],
[0.5, 0.5, 0.5, 0.5, 0.58, 0.67, 0.75, 0.83, 0.92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.92, 0.83, 0.75, 0.67, 0.58, 0.5],
[0.5, 0.5, 0.5, 0.5, 0.58, 0.67, 0.75, 0.83, 0.92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.92, 0.83, 0.75, 0.67, 0.58, 0.5],
[0.5, 0.5, 0.5, 0.5, 0.58, 0.67, 0.75, 0.83, 0.92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.92, 0.83, 0.75, 0.67, 0.58, 0.5],
[0.5, 0.5, 0.5, 0.5, 0.58, 0.67, 0.75, 0.83, 0.92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.92, 0.83, 0.75, 0.67, 0.58, 0.5],
[0.5, 0.5, 0.5, 0.5, 0.58, 0.67, 0.75, 0.83, 0.92, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.92, 0.83, 0.75, 0.67, 0.58, 0.5],
[0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.55, 0.55, 0.55, 0.6, 0.6, 0.6, 0.6, 0.55, 0.55, 0.55, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5]
],
"nowCell": { "dow": 2, "hour": 16 }
}
The schedule is not in effect — the model has none, or off-peak hours are disabled on the platform:
{
"enabled": false,
"timezone": null,
"currentMultiplier": 1,
"nextWindow": null,
"currentWindowEndsInHours": null,
"grid": null,
"nowCell": null
}
Error response example
401 MISSING_API_KEY — the X-Api-Key header is missing:
{
"success": false,
"error": {
"code": "MISSING_API_KEY",
"message": "API key required. Pass via X-Api-Key header."
}
}
Errors
| HTTP | Code | Description |
|---|---|---|
| 401 | MISSING_API_KEY |
The X-Api-Key header is missing |
| 401 | INVALID_API_KEY |
The key is not recognized — no such key exists on the platform |
| 403 | scope_missing |
The API key lacks the vibe:ai scope |
| 429 | rate_limit_exceeded |
AI endpoint request limit exceeded. Time until reset is in the Retry-After header |
The full list of common API errors — Errors.
Known specifics
The multiplier is a consumption coefficient, not the discount size. A value of 0.62 means the call takes 62% of the quota share it would take without a discount, that is, consumption is reduced by 38%. The discount size is computed as 1 - currentMultiplier.
Specify the model. Without the model parameter the endpoint returns the schedule the platform marked as the main one. Until a main schedule is assigned, such a call returns enabled: false even when individual models do have discounts.
An unknown model does not return an error. If a model has no schedule or the identifier does not exist, the endpoint returns 200 with enabled: false and empty fields. The presence of discounts is signaled by the enabled field, not by the response code.
The schedule changes. The platform recomputes the grid based on actual load, so re-read the schedule before planning rather than storing it for a long time.
nextWindow looks for a strictly cheaper hour. If the current hour is already the cheapest within the next week, the field is null. This does not mean there is no discount — check currentMultiplier.
The discount also slows how fast the pacing windows fill. The daily and weekly windows grow by the same discounted amount as the monthly quota. Moving deferrable jobs into the off-peak hours postpones both the 429 ai_pacing_limited response and the exhaustion of the monthly quota.