# Scheduling
source: https://docs.chalk.ai/docs/functions/scheduling

## Run a Chalk Function on a recurring schedule with a crontab expression or a duration string.

Some jobs run on a clock instead of in response to a request: refreshing a
table of exchange rates, recomputing a daily rollup, warming a cache,
syncing an external source.
A scheduled function is a Function that Chalk invokes for you on a recurring
schedule: declare a schedule in the decorator, and Chalk runs it at the
cadence you specify, with the image, resources, secrets, and retry behavior
you configured. There is no separate cron server to operate.

```
import chalkcompute

@chalkcompute.function(
    schedule="0 * * * *",                      # every hour, on the hour
    secrets=[chalkcompute.Secret.from_chalk_env("EXCHANGE_API_KEY")],
    cpu="1",
    memory="2Gi",
    retries=3,
)
def refresh_exchange_rates() -> None:
    import os
    import requests

    resp = requests.get(
        "https://api.example.com/v1/rates",
        params={"base": "USD"},
        headers={"Authorization": f"Bearer {os.environ['EXCHANGE_API_KEY']}"},
    )
    rates = resp.json()["rates"]
    # write the latest rates to your store
    ...
```

schedule and retries govern how and when the platform invokes the
function; everything else configures the Scaling Group that hosts it, exactly
as it would for an on-demand function. Scheduled jobs often do heavier work,
such as nightly batch inference or periodic re-scoring of a dataset, and can
request a GPU the same way:

```
@chalkcompute.function(
    schedule="0 3 * * *",      # daily at 03:00 UTC
    gpu="nvidia-l4",
    cpu="4",
    memory="16Gi",
)
def nightly_batch_inference() -> None:
    ...
```

### Schedule syntax

schedule accepts either a crontab expression or a Chalk duration string. Use
whichever reads more clearly for your cadence. All schedules are evaluated
in UTC.

Crontab: five space-separated fields (minute hour day-of-month month
day-of-week). Standard syntax is supported: *, lists (1,15), ranges
(1-5), and steps (*/10).

| Expression       | Runs                      |
| ---------------- | ------------------------- |
| `"*/10 * * * *"` | every 10 minutes          |
| `"0 * * * *"`    | every hour, on the hour   |
| `"0 2 * * *"`    | every day at 02:00 UTC    |
| `"0 0 * * 0"`    | every Sunday at 00:00 UTC |

Duration shorthand: a single interval, for simple cadences. Supported units
are minutes (≤ 60), hours (≤ 24), and days (= 1), written as one value:

| Shorthand | Runs             | Equivalent crontab |
| --------- | ---------------- | ------------------ |
| `"10m"`   | every 10 minutes | `*/10 * * * *`     |
| `"6h"`    | every 6 hours    | `0 */6 * * *`      |
| `"1d"`    | every day        | `0 0 * * *`        |

Shorthand intervals are aligned to clock boundaries, not to your deployment
time: "6h" runs at 00:00, 06:00, 12:00, and 18:00 UTC. Anything longer or
compound ("90m", "1h30m", "2d") must be written as a crontab expression.

```
@chalkcompute.function(schedule="0 2 * * *")   # nightly at 02:00 UTC
def nightly_rollup() -> None:
    ...

@chalkcompute.function(schedule="15m")         # every 15 minutes
def warm_cache() -> None:
    ...
```

### Rules for scheduled functions

Because a scheduled function is invoked by the platform rather than a caller,
two constraints apply and are enforced when the function is defined:

- No inputs. A scheduled function must take no arguments. Have it read its
own inputs from your sources.
- Not a generator. A scheduled run has no caller to consume yielded
values, so a scheduled function cannot yield. Return None (or a value)
instead.

Everything else about a Compute Function still applies: custom images,
resource requests, secrets, and retries all work exactly as they do for
on-demand functions.

### Observing runs

Each invocation appears in the Chalk dashboard with its status, logs, and
resource usage, just like a manually invoked function. Emit logs from the
body to make each run observable:

```
from chalk import chalk_logger

@chalkcompute.function(schedule="1h")
def refresh_exchange_rates() -> None:
    chalk_logger.info("refresh started")
    rates = ...  # fetch the latest rates and write them to your store
    chalk_logger.info("refreshed %s rates", len(rates))
```





