Nicholas Pini
07/22/2026, 11:09 AMMarvin
07/22/2026, 11:09 AMMarvin
07/22/2026, 11:11 AMpython
from datetime import timedelta
from prefect import flow
@flow
def my_flow():
print("running")
if __name__ == "__main__":
my_flow.deploy(
name="every-10-minutes",
work_pool_name="my-work-pool",
interval=timedelta(minutes=10),
)
You can also use seconds:
python
interval=600
Or a cron schedule if you prefer cron syntax:
python
from prefect import flow
@flow
def my_flow():
print("running")
if __name__ == "__main__":
my_flow.deploy(
name="every-10-minutes",
work_pool_name="my-work-pool",
cron="*/10 * * * *",
)
Recommendation: for “every 10 minutes”, interval=timedelta(minutes=10) is the clearest option.
A couple of notes:
- deploy() creates/applies the deployment in Prefect 3.x
- you’ll need a worker running for the deployment’s work pool
- if you want multiple schedules or more control, use the schedules= argument
Relevant docs:
- Deploy via Python
- Create schedules
If you want, I can also show the exact version for:
- serverless / managed execution
- Docker work pools
- Kubernetes work pools
- or CI/CD (like CodeBuild)Nicholas Pini
07/22/2026, 11:12 AMMarvin
07/22/2026, 11:12 AMMarvin
07/22/2026, 11:13 AMpython
from datetime import timedelta
from prefect.client.orchestration import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def set_deployment_to_every_10_minutes():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
schedules = await client.read_deployment_schedules(deployment.id)
if not schedules:
print("This deployment has no schedules to update.")
return
schedule = schedules[0]
await client.update_deployment_schedule(
deployment_id=deployment.id,
schedule_id=schedule.id,
schedule=IntervalSchedule(interval=timedelta(minutes=10)),
active=True,
)
print("Updated deployment schedule to every 10 minutes.")
A few important notes:
- read_deployment_by_name() expects "<flow-name>/<deployment-name>"
- read_deployment_schedules() returns the schedules attached to that deployment
- update_deployment_schedule() updates one existing schedule
- if the deployment has multiple schedules, you may want to update all of them, or pick the right one by slug
If you want to update all existing schedules on the deployment:
python
from datetime import timedelta
from prefect.client.orchestration import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def set_all_deployment_schedules_to_every_10_minutes():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
schedules = await client.read_deployment_schedules(deployment.id)
for schedule in schedules:
await client.update_deployment_schedule(
deployment_id=deployment.id,
schedule_id=schedule.id,
schedule=IntervalSchedule(interval=timedelta(minutes=10)),
active=True,
)
print(f"Updated {len(schedules)} schedule(s).")
If the deployment has no schedule yet, then you’d need to create one instead of updating one. If that’s your case, say so and I can give you the exact code for adding a schedule to an existing deployment.
Docs:
- Deployment client API
- Create schedulesNicholas Pini
07/22/2026, 11:42 AMMarvin
07/22/2026, 11:42 AMMarvin
07/22/2026, 11:44 AMbash
prefect deployment run "my-flow/my-deployment"
That can be done from:
- cron
- Kubernetes CronJob
- GitHub Actions scheduled workflow
- AWS EventBridge + Lambda / ECS task / CodeBuild
- any other scheduler you already trust
In Python, the equivalent is:
python
from prefect.deployments import run_deployment
run_deployment(name="my-flow/my-deployment")
Or with the client API:
python
from prefect.client.orchestration import get_client
async def trigger():
async with get_client() as client:
deployment = await client.read_deployment_by_name("my-flow/my-deployment")
await client.create_flow_run_from_deployment(deployment.id)
Why this is good for your case: redeploying the deployment does not affect the external scheduler.
2. Use a Prefect Automation
Automations are separate from deployment definitions, so they generally survive redeploys better than deployment schedules.
An automation can run a deployment as an action. That said, for a pure “every 10 minutes” heartbeat-style schedule, an external scheduler is often simpler than trying to model time-based recurrence through automations.
Docs:
- Automations
- Run deployments
3. Change your CI/CD so it does not overwrite schedules
If you want to keep using deployment-native schedules, another option is to make your deployment config the source of truth and include the schedule in CI/CD itself. But from your description, it sounds like you specifically want to avoid that.
My recommendation for your setup
Since you’re deploying from CI/CD, I’d strongly recommend:
- deploy the flow without a schedule
- use an external scheduler to call prefect deployment run "my-flow/my-deployment" every 10 minutes
That keeps:
- deployment definition = managed by CI/CD
- execution cadence = managed independently
If you want, I can give you a ready-to-use example for:
- AWS EventBridge
- CodeBuild
- GitHub Actions
- Kubernetes CronJob
- or plain cronNicholas Pini
07/22/2026, 1:44 PMMarvin
07/22/2026, 1:44 PMMarvin
07/22/2026, 1:45 PMcreate_deployment_schedules(...).
That appends a schedule; it does not replace existing ones.
Example for every 10 minutes:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.actions import DeploymentScheduleCreate
from prefect.client.schemas.schedules import IntervalSchedule
async def add_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
created = await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
DeploymentScheduleCreate(
schedule=IntervalSchedule(interval=timedelta(minutes=10)),
active=True,
slug="every-10-minutes",
)
],
)
print(created)
If you prefer cron syntax instead:
python
from prefect.client import get_client
from prefect.client.schemas.actions import DeploymentScheduleCreate
from prefect.client.schemas.schedules import CronSchedule
async def add_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
DeploymentScheduleCreate(
schedule=CronSchedule(cron="*/10 * * * *", timezone="UTC"),
active=True,
slug="every-10-minutes",
)
],
)
A few notes:
- slug is optional, but useful if you want to refer to this schedule later
- read_deployment_by_name() expects "<flow-name>/<deployment-name>"
- this adds the schedule outside the deployment spec, which can help if your CI/CD deployment process is replacing the deployment definition
Docs:
- Deployment client API
- Create schedules
If you want, I can also give you:
- a sync version
- a version that checks whether the slug already exists first
- or a small utility that re-adds the schedule after CI/CD deploysNicholas Pini
07/22/2026, 1:47 PMlist[tuple[SCHEDULE_TYPES, bool]] for the schedules parameterMarvin
07/22/2026, 1:47 PMMarvin
07/22/2026, 1:49 PMcreate_deployment_schedules() accepts either:
- list[tuple[SCHEDULE_TYPES, bool]]
- list[DeploymentScheduleCreate]
So for your version, this is valid and probably the simplest way to add a new schedule to an existing deployment:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def add_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
(
IntervalSchedule(interval=timedelta(minutes=10)),
True,
)
],
)
Or with cron:
python
from prefect.client import get_client
from prefect.client.schemas.schedules import CronSchedule
async def add_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
(
CronSchedule(cron="*/10 * * * *", timezone="UTC"),
True,
)
],
)
A useful nuance:
- the tuple form is shorthand for (schedule, active)
- if you want per-schedule slug, parameters, or max_scheduled_runs, use DeploymentScheduleCreate
- if you pass slug=... or parameters=... to create_deployment_schedules(...) with the tuple form, those values get applied uniformly during conversion
So for just add a 10-minute schedule, the tuple form is perfectly fine.
Docs:
- Create schedules
- Deployment client API
If you want, I can give you a tiny helper like ensure_schedule_exists(...) so your CI/CD can safely re-add the schedule only if it’s missing.Nicholas Pini
07/22/2026, 1:51 PMdef create_deployment_schedules(
self,
deployment_id: UUID,
schedules: list[tuple["SCHEDULE_TYPES", bool]],
) -> list["DeploymentSchedule"]:
"""
Create deployment schedules.
Args:
deployment_id: the deployment ID
schedules: a list of tuples containing the schedule to create
and whether or not it should be active.
Raises:
RequestError: if the schedules were not created for any reason
Returns:
the list of schedules created in the backend
"""
from prefect.client.schemas.actions import DeploymentScheduleCreate
from prefect.client.schemas.objects import DeploymentSchedule
deployment_schedule_create = [
DeploymentScheduleCreate(schedule=schedule[0], active=schedule[1])
for schedule in schedules
]
json = [
deployment_schedule_create.model_dump(mode="json")
for deployment_schedule_create in deployment_schedule_create
]
response = self.request(
"POST",
"/deployments/{id}/schedules",
path_params={"id": deployment_id},
json=json,
)
return DeploymentSchedule.model_validate_list(response.json())Marvin
07/22/2026, 1:51 PMMarvin
07/22/2026, 1:51 PMschedules: list[tuple[SCHEDULE_TYPES, bool]]
So to add a 10-minute schedule to an existing deployment in your environment, use:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def add_ten_minute_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
(
IntervalSchedule(interval=timedelta(minutes=10)),
True,
)
],
)
Or with cron:
python
from prefect.client import get_client
from prefect.client.schemas.schedules import CronSchedule
async def add_ten_minute_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
(
CronSchedule(cron="*/10 * * * *", timezone="UTC"),
True,
)
],
)
So the key point is:
- you do not pass DeploymentScheduleCreate
- Prefect constructs DeploymentScheduleCreate internally from each (schedule, active) tuple
Thanks for the correction — I should have matched your exact installed API rather than a broader variant.
If helpful, I can also give you:
- a sync version
- code to list existing schedules first
- or an ensure_10_min_schedule() helper that avoids adding duplicatesNicholas Pini
07/22/2026, 1:52 PMMarvin
07/22/2026, 1:52 PMMarvin
07/22/2026, 1:52 PMcreate_deployment_schedules().
From the function you pasted, each tuple is converted internally like this:
python
DeploymentScheduleCreate(schedule=schedule[0], active=schedule[1])
So only these two fields are populated:
- schedule
- active
That means:
- no slug
- no custom parameters
- no max_scheduled_runs
So the answer is:
The slug will be auto-generated by the backend, or left unset depending on how that backend version handles schedule creation — but you do not control it through this client method.
If you need to know what was created, capture the returned schedules:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def add_schedule():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
created = await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[
(
IntervalSchedule(interval=timedelta(minutes=10)),
True,
)
],
)
for s in created:
print("id:", s.id)
print("slug:", s.slug)
print("active:", s.active)
print("schedule:", s.schedule)
If your real goal is “add this exact schedule only once”, then since you can’t rely on setting a slug, the practical approach is:
1. read the deployment’s existing schedules
2. compare their schedule definitions
3. only create the new one if an equivalent 10-minute schedule is not already present
If you want, I can write you a small ensure_interval_schedule(deployment_name, minutes=10) helper for your exact API version.Nicholas Pini
07/22/2026, 1:53 PMMarvin
07/22/2026, 1:53 PMMarvin
07/22/2026, 1:53 PMDeploymentScheduleCreate is the request schema the client sends to the API.
In the code you pasted, the public method accepts a simplified input:
python
list[tuple[SCHEDULE_TYPES, bool]]
and then internally converts each tuple into:
python
DeploymentScheduleCreate(schedule=..., active=...)
So its purpose is still real — it’s just not exposed as part of that particular method’s public interface in your version.
In other words:
- you call create_deployment_schedules(..., schedules=[(schedule, active)])
- the client builds DeploymentScheduleCreate(...)
- the API receives serialized DeploymentScheduleCreate payloads
Why have the model at all?
1. It defines the API payload shape
It is the canonical schema for “create a deployment schedule”.
2. It validates data before sending
The client can ensure the outgoing payload matches what the server expects.
3. It is reused elsewhere in the codebase
Even if this specific helper hides it, the schema is still useful internally and in other code paths.
4. It leaves room for richer APIs in other versions
Some versions expose richer creation methods; others expose only the tuple shorthand.
So in your version, DeploymentScheduleCreate is mostly an internal transport/validation model for this method, not something you pass directly.
That’s also why the mismatch feels odd: the underlying schema supports fields like slug, but this convenience method only exposes:
- schedule
- active
So effectively, the wrapper is narrower than the underlying API model.
If you want, I can help check whether:
- there is another client method in 3.6.12 that exposes slug
- or whether the only practical route in your version is using the raw REST API directly.Nicholas Pini
07/22/2026, 1:54 PMMarvin
07/22/2026, 1:54 PMMarvin
07/22/2026, 1:56 PMslug
- the UI talks to that backend API directly and can send slug
- but the Python SDK method in your installed version exposes only:
python
schedules: list[tuple[SCHEDULE_TYPES, bool]]
so it only lets you provide:
- the schedule
- whether it is active
In other words, the UI and API are richer than the convenience wrapper you have in the SDK.
So the short answer is:
You can do it in the UI because the API supports it; you cannot do it through that Python helper because your SDK version does not expose the field.
If you want to do it from Python without upgrading, the workaround is to call the underlying API through the client’s lower-level request() method.
Example:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def add_schedule_with_slug():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
payload = [
{
"schedule": IntervalSchedule(
interval=timedelta(minutes=10)
).model_dump(mode="json"),
"active": True,
"slug": "every-10-minutes",
}
]
response = await client.request(
"POST",
"/deployments/{id}/schedules",
path_params={"id": deployment.id},
json=payload,
)
return response.json()
If your client is sync instead of async, the shape is the same, just without await.
So your options are:
1. Keep using the SDK helper
Easy, but no slug
2. Use client.request(...) directly
Lets you send slug
3. Upgrade to a newer Prefect version
Newer versions expanded the schedule client methods to expose more fields
Given your CI/CD setup, option 2 is probably the most practical if you need slug now.
If you want, I can give you a fully correct helper for your version that:
- reads the deployment by name
- posts a schedule with slug
- returns the created schedule object cleanlyNicholas Pini
07/22/2026, 2:00 PMMarvin
07/22/2026, 2:00 PMMarvin
07/22/2026, 2:00 PMcreate_deployment_schedules() helper in Prefect 3.6.12, that is correct.
From the function you pasted, the SDK only exposes:
- schedule
- active
So when adding a schedule to an existing deployment via that method, you cannot set:
- slug
- parameters
- max_scheduled_runs
through the public Python helper.
That’s why the UI can do more than that SDK method in your version.
So specifically:
python
await client.create_deployment_schedules(
deployment_id=deployment.id,
schedules=[(my_schedule, True)],
)
does not let you attach schedule-level parameter overrides.
If you need schedule parameters in 3.6.12, your choices are:
1. Use the UI
Works because it hits the API directly
2. Use the low-level client request
You can send the full payload yourself, including parameters
Example:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.schedules import IntervalSchedule
async def add_schedule_with_parameters():
async with get_client() as client:
deployment = await client.read_deployment_by_name(
"my-flow/my-deployment"
)
payload = [
{
"schedule": IntervalSchedule(
interval=timedelta(minutes=10)
).model_dump(mode="json"),
"active": True,
"slug": "every-10-minutes",
"parameters": {
"foo": "bar",
"x": 123
},
"max_scheduled_runs": 100,
}
]
response = await client.request(
"POST",
"/deployments/{id}/schedules",
path_params={"id": deployment.id},
json=payload,
)
return response.json()
3. Upgrade Prefect
Later versions expose more of this through the SDK method itself
So the answer to your question is:
Yes — in Prefect 3.6.12, the high-level Python SDK method for adding deployment schedules does not let you set schedule parameters.
If you want, I can turn the raw client.request(...) approach into a small reusable helper like:
python
add_deployment_schedule(
deployment_name="flow/deployment",
every_minutes=10,
slug="every-10-minutes",
parameters={"foo": "bar"},
)Nicholas Pini
07/22/2026, 2:02 PMwith get_client(sync_client=True) as client:
deployment = client.read_deployment_by_name("fire-sight-polling/fire-sight")
client.update_deployment(
deployment.id,
DeploymentUpdate(
schedules=[DeploymentScheduleUpdate(schedule=IntervalSchedule(interval=timedelta(minutes=10)), slug="test")]
),
)Marvin
07/22/2026, 2:02 PMMarvin
07/22/2026, 2:02 PMslug via Python, using:
python
client.update_deployment(
deployment.id,
DeploymentUpdate(
schedules=[
DeploymentScheduleUpdate(
schedule=IntervalSchedule(interval=timedelta(minutes=10)),
slug="test",
)
]
),
)
is the way to do it.
Your full example:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.actions import DeploymentUpdate
from prefect.client.schemas.schedules import DeploymentScheduleUpdate, IntervalSchedule
with get_client(sync_client=True) as client:
deployment = client.read_deployment_by_name("fire-sight-polling/fire-sight")
client.update_deployment(
deployment.id,
DeploymentUpdate(
schedules=[
DeploymentScheduleUpdate(
schedule=IntervalSchedule(interval=timedelta(minutes=10)),
slug="test",
)
]
),
)
Important behavior note:
- this goes through update_deployment(...)
- DeploymentUpdate.schedules lets you provide schedule objects with richer fields
- this is different from create_deployment_schedules(...), which in your installed version is the narrower tuple-based helper
One thing to be careful about:
update_deployment(..., DeploymentUpdate(schedules=[...])) may replace the deployment’s schedules rather than append one alongside existing schedules.
So if your goal is specifically add a new schedule without removing existing ones, the safe pattern is:
1. read the existing deployment
2. take its current schedules
3. append your new DeploymentScheduleUpdate(...)
4. send the full combined list back in DeploymentUpdate(...)
If you want, I can help write a small append_schedule_to_deployment(...) helper that:
- preserves existing schedules
- avoids duplicate slugs
- works in sync or async mode
And thank you for the correction — that’s a useful nuance for 3.6.12.Nicholas Pini
07/22/2026, 2:03 PMupdate_deployment(..., DeploymentUpdate(schedules=[...])) may replace the deployment’s schedules rather than append one alongside existing schedules.it doesn't, I have just tested it
Marvin
07/22/2026, 2:03 PMMarvin
07/22/2026, 2:03 PMschedules=[...] to update_deployment(...) adds the schedule rather than replacing all existing schedules.
So the corrected guidance is:
- create_deployment_schedules(...) is a narrow helper
- update_deployment(..., DeploymentUpdate(schedules=[DeploymentScheduleUpdate(...)])) is the more capable Python SDK path in your version
- and in your testing, it does not wipe existing schedules
So for adding a 10-minute schedule with a slug, this is a valid pattern:
python
from datetime import timedelta
from prefect.client import get_client
from prefect.client.schemas.actions import DeploymentUpdate
from prefect.client.schemas.schedules import (
DeploymentScheduleUpdate,
IntervalSchedule,
)
with get_client(sync_client=True) as client:
deployment = client.read_deployment_by_name("fire-sight-polling/fire-sight")
client.update_deployment(
deployment.id,
DeploymentUpdate(
schedules=[
DeploymentScheduleUpdate(
schedule=IntervalSchedule(interval=timedelta(minutes=10)),
slug="test",
)
]
),
)
And based on what you found, this route also lets you set things the narrower helper does not, such as:
- slug
- schedule details beyond the tuple shorthand
- potentially other schedule-level fields supported by DeploymentScheduleUpdate
So the practical lesson here is:
In Prefect 3.6.12, for adding richer schedules to an existing deployment from Python, update_deployment(... DeploymentUpdate(schedules=[...])) is the better SDK route than create_deployment_schedules(...).
Thanks again — that’s a genuinely important distinction.