# Project Scheduler: Gantt Chart, Critical Path & Leveling (`jumblemog/project-scheduler`) Actor

Turn a task list into a dated schedule: dependencies with lags, working calendars with public holidays, critical path and float, resource leveling, finish-date odds, costs. Gantt chart PNG/PDF, Excel, MS Project XML, CSV, iCal, Mermaid. Text, JSON or CSV. For AI agents.

- **URL**: https://apify.com/jumblemog/project-scheduler.md
- **Developed by:** [Jumble Mog](https://apify.com/jumblemog) (community)
- **Categories:** AI, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Project Scheduler: Gantt Chart, Critical Path & Leveling

**Turn a task list into a dated project schedule, with the critical path, a Gantt chart and an MS Project file.** Write the tasks as short lines like `Backend: 8-12d after Design; needs 2 developers`, or send JSON, a CSV, an Excel file or a Google Sheet, and it works out **when every task starts and finishes on a real working calendar** (your working days and hours, **public holidays for over 100 countries**, company closures, people's days off). You get the **critical path**, every task's **total and free float**, **resource leveling** so nobody is booked twice (solved with Google's [OR-Tools](https://developers.google.com/optimization) CP-SAT), **who does what** inside each team, **costs** from hourly or day rates, whether the **deadline** is met, and, when tasks have ranges, the **finish-date odds** (P50, P80, P90, P95) from a Monte Carlo simulation. Files come with every schedule: a **Gantt chart (PNG, SVG and a paged PDF)**, an **Excel workbook** with a cell-colored Gantt sheet, **MS Project XML** that opens in MS Project, ProjectLibre and GanttProject, **CSV**, an **iCalendar (.ics)** file, a **Mermaid** gantt chart and a **Markdown** report. Made for project managers, construction and event planners, agencies, consultants and software teams, for developers who need a scheduling API, and for AI agents that need a real schedule from one API call.

![A Gantt chart made by this Actor for a website launch: phases Plan, Build and Launch with seven tasks, critical tasks in red, a non-critical task in blue with its float drawn as a gray line, dependency arrows, weekends and US holidays shaded, the resources named next to each bar, the Go live milestone as a diamond and the December 18 deadline as a dashed red line. The summary reads: 7 tasks from 2026-11-02 to 2026-12-14 (29 working days); 6 on the critical path, leveled for Developers; meets the 2026-12-18 deadline; 80% chance of finishing by 2026-12-15](https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/project-scheduler-example.png?signature=1rEviAoJyjIKTGNNSxmqN)

### What it does

- **Dates on a real calendar**: durations in working days, hours, weeks or months, counted over your working week (Mon-Fri, Sun-Thu, six days or every day), your hours per day, public holidays by country or region (`US`, `GB-SCT`, `DE-BY`, `IN`), your own closures and each person's days off.
- **Every kind of dependency**: finish-to-start, start-to-start, finish-to-finish and start-to-finish, with lags and leads in time or in percent (`after Design +2d`, `starts with Framing`, `3FS+1d`, `Build FF+20%`), "not before" dates and task deadlines. A phase can depend on a phase.
- **Critical path method (CPM)**: early and late dates, total float and free float for every task, and the critical path in order. A deadline that can't be met shows up as negative float on the tasks that cause it.
- **Resource leveling**: people, teams (`Developers: 3`) and equipment with part-time assignments (`Dana (50%)`). When someone is overbooked, OR-Tools CP-SAT finds the earliest finish date with nobody booked twice, then pulls every task as early as it can go, and says when that finish is proven the earliest possible. Days off are respected, and team work is dealt out to team members (`Developers 1`, `Developers 2`).
- **Finish-date odds**: give tasks ranges (`3-5d`) or best/likely/worst estimates (`3/5/10d`), or a general uncertainty, and a Monte Carlo simulation (PERT distributions, 2,000 runs by default) gives the P50, P80, P90 and P95 finish dates, the chance of meeting the deadline and each task's chance of being critical.
- **Costs**: hourly or day rates per person or team, fixed task costs, and totals per task, phase and project.
- **Phases and WBS**: headings group tasks into phases and sub-phases with WBS numbers, phase dates and rollups.
- **Files for the team and the client**: a Gantt chart (PNG, SVG, PDF with every row), Excel (schedule, Gantt, resources, odds, holidays), MS Project XML (MSPDI), CSV, iCalendar for Google Calendar, Outlook or Apple Calendar, Mermaid for GitHub, Notion or a chat reply, and a Markdown report.
- **Reads what you have**: plain text lines, JSON objects with common field names, CSV or TSV, Excel files or a Google Sheet link, including exports with MS Project style predecessors.
- **Many projects in one run**: up to 50, each with its own tasks, resources and calendar.

### Who it's for

- **Project and program managers**: a quick, checked schedule and Gantt chart from a task list, with the critical path, float and deadline risk, without opening desktop software.
- **Construction, renovation and trades**: phase plans with crews and subcontractors, inspections, lead times and holidays, exported to MS Project or ProjectLibre.
- **Event, marketing and launch planners**: work back from a date and see whether the deadline holds and how likely it is.
- **Agencies and consultants**: proposal timelines with costs per phase and a client-ready Gantt PDF.
- **Software and product teams**: release plans with team capacity, ranged estimates and P80 dates instead of single guesses.
- **Developers and no-code builders**: a project scheduling API for apps, CRMs, ticket systems and Make, n8n or Zapier flows, billed per schedule.
- **AI agents and LLM apps**: language models can't do calendar arithmetic, critical paths or leveling reliably. An agent can send the plan as plain text and get back dates it can trust, plus files to hand to a person.

### How to use it

1. List the tasks in **Tasks**, one per line, such as `Design: 5d after Requirements; by Dana`. The form starts with a website launch, so you can click **Start** right away.
2. List people and teams in **Resources**, such as `Developers: 2, $95/h` or `Dana: off 2026-11-23 to 2026-11-27`.
3. Set the **Start date**, **Working days**, **Holidays** (a country code such as `US`) and an optional **Deadline**.
4. Click **Start**. The **Schedules** view shows the Gantt chart, summary and files, **Tasks** every task with its dates and float, **Resources** each person's work and cost, **Milestones** and **Phases** their dates.

You can also run it through the [Apify API](https://docs.apify.com/api/v2), from Make, n8n, Zapier and other integrations, or as a tool for AI agents.

### Writing tasks

One task per line: a name, a colon, then the duration and anything else, separated by semicolons or commas.

| You write | It means |
|---|---|
| `Design: 5d` | 5 working days. Also `4h`, `2w`, `1mo` (4 working weeks), `30m`, `1.5d`. |
| `Backend: 8-12d` | A range: planned at 10 days, simulated between 8 and 12. |
| `Testing: 3/5/10d` | Best, likely and worst case: planned at 5 days. |
| `Go live: milestone` | A milestone (0 days). |
| `after Design, Content` | Starts when both finish (finish-to-start). |
| `after Design +2d` | Two working days after Design finishes; `-1d` overlaps by a day. |
| `starts with Framing` or `SS Framing` | Start-to-start; `finishes with Framing` or `FF Framing` is finish-to-finish. |
| `after 3FS+2d, 4SS` | MS Project style: task 3 plus two days, start-to-start with task 4. |
| `after Build` | When `Build` is a phase: after its last tasks. |
| `by Dana`, `by Dana and Lee` | Assigned people. |
| `needs 2 developers`, `uses crane` | Two members of the Developers team; one crane. |
| `by Dana (50%)` | Dana at half time. |
| `not before 2026-11-09` | Can't start earlier (materials arrive, permit date). |
| `deadline 2026-12-04` | Must finish by then; shows negative float if it can't. |
| `$500` or `cost $500` | A fixed cost for the task. |
| `## Build` or `Build:` alone | A heading: the tasks below form the Build phase. `## Build: after Plan` makes the whole phase wait. |

Lines may start with `-`, `*` or a number (`3. Pour slab: 1d`); numbers become task IDs for `after 3`. Durations without a unit are days.

**Resources**, one per line: `Dana`, `Developers: 3` (a team of three), `$95/h` or `$600/day`, and `off 2026-12-21 to 2026-12-31`. People named on tasks but not listed are taken as one person (or as many as one task needs), and the result says so.

**Tables** (CSV, JSON, Excel, Google Sheets) need a task name and a duration column; other columns are optional and many spellings work: `ID`, `Task`, `Duration` (or `Days`, `Hours`, `Weeks`), `Predecessors`, `Resources` (`Dana, Developer[200%]`), `Optimistic`/`Most likely`/`Pessimistic`, `Start no earlier than`, `Deadline`, `Phase`, `Milestone`, `Cost`, `Notes`.

### Examples

**A product launch with a team, ranges and a deadline** (the form's example):

```json
{
  "title": "Website launch",
  "tasks": ["## Plan", "Requirements: 3d; by Dana", "Design: 5d after Requirements; by Dana",
            "## Build", "Backend: 8-12d after Design; needs 2 developers", "Frontend: 6d after Design; needs 1 developer",
            "Content: 4d, starts with Frontend; by Lee",
            "## Launch", "Testing: 5d after Backend, Frontend; by QA", "Go live: milestone after Testing"],
  "resources": ["Developers: 2, $95/h", "Dana: $80/h, off 2026-11-23 to 2026-11-27", "Lee: $60/h", "QA: $70/h"],
  "startDate": "2026-11-02", "holidays": ["US"], "deadline": "2026-12-18"
}
```

Backend needs both developers, so it can't run beside Frontend: leveling puts Frontend first and adds six working days. The project finishes on 2026-12-14 (Veterans Day and Thanksgiving skipped), four working days before the deadline, with an 80% chance of finishing by 2026-12-15.

**A house build from a table with MS Project style predecessors:**

```json
{
  "title": "House build",
  "tasksCsv": "ID,Task,Duration,Predecessors,Resources\n1,Permits,10 days,,\n2,Foundation,5 days,1,Crew\n3,Framing,10 days,2FS+3d,Crew\n4,Roofing,4 days,3,Roofers\n5,Electrical,6 days,3SS+5d,Electrician\n6,Plumbing,5 days,3SS+5d,Plumber\n7,Drywall,6 days,\"4,5,6\",Crew\n8,Finishing,8 days,7,Crew\n9,Handover,0 days,8,",
  "startDate": "2026-11-02"
}
```

46 working days; the critical path runs Permits, Foundation, Framing, Roofing, Drywall, Finishing, Handover, and Electrical has 3 days of float.

**An office move in the UK with uncertainty on every task:**

```json
{
  "title": "Office move",
  "tasks": ["Find space: 3w", "Sign lease: 1w after Find space", "Fit-out: 6w after Sign lease; needs 2 fitters",
            "IT cabling: 2w after Sign lease", "Order furniture: 1d after Sign lease",
            "Furniture delivery: 4w after Order furniture", "Move: 2d after Fit-out, IT cabling, Furniture delivery"],
  "resources": ["Fitters: 2, £300/day"],
  "startDate": "2026-11-02", "holidays": ["GB-ENG", "2026-12-24 to 2027-01-01"],
  "uncertaintyPercent": 25, "deadline": "2027-04-30"
}
```

**Several projects in one call:** put each in `jobs`, such as `{"jobs": [{"title": "Audit", "tasks": [...]}, {"title": "Training", "tasks": [...]}], "holidays": ["US"]}`. Settings outside `jobs` apply to every project that doesn't set its own.

### How-to examples

These are the examples under **Examples** above, with what their runs gave.

#### How to make a Gantt chart from a task list

```json
{"title": "Website launch",
 "tasks": ["## Plan", "Requirements: 3d; by Dana", "Design: 5d after Requirements; by Dana",
           "## Build", "Backend: 8-12d after Design; needs 2 developers", "Frontend: 6d after Design; needs 1 developer",
           "Content: 4d, starts with Frontend; by Lee",
           "## Launch", "Testing: 5d after Backend, Frontend; by QA", "Go live: milestone after Testing"],
 "resources": ["Developers: 2, $95/h", "Dana: $80/h, off 2026-11-23 to 2026-11-27", "Lee: $60/h", "QA: $70/h"],
 "startDate": "2026-11-02", "holidays": ["US"], "deadline": "2026-12-18"}
```

Result: a dated schedule with a Gantt chart. Backend needs both developers, so leveling puts Frontend first and adds six working days. The project finishes on 2026-12-14, with Veterans Day and Thanksgiving skipped and four working days to spare before the deadline, and an 80% chance of finishing by 2026-12-15.

#### How to find the critical path of a project

```json
{"title": "House build",
 "tasksCsv": "ID,Task,Duration,Predecessors,Resources\n1,Permits,10 days,,\n2,Foundation,5 days,1,Crew\n3,Framing,10 days,2FS+3d,Crew\n4,Roofing,4 days,3,Roofers\n5,Electrical,6 days,3SS+5d,Electrician\n6,Plumbing,5 days,3SS+5d,Plumber\n7,Drywall,6 days,\"4,5,6\",Crew\n8,Finishing,8 days,7,Crew\n9,Handover,0 days,8,",
 "startDate": "2026-11-02"}
```

Result: 46 working days. The critical path runs Permits, Foundation, Framing, Roofing, Drywall, Finishing, Handover, and Electrical has 3 days of float. MS Project style predecessors such as `2FS+3d` and `3SS+5d` are read as they are.

#### How to estimate the odds of finishing by a deadline

Give tasks ranges (`8-12d`), three-point estimates (`3/5/10d`) or a general `uncertaintyPercent`, and a `deadline`. A Monte Carlo simulation of 2,000 runs gives the P50, P80, P90 and P95 finish dates and the chance of meeting the deadline, as in the office move example above.

#### How to let an AI agent plan a project

Add this Actor to the [Apify MCP server](https://mcp.apify.com) with `https://mcp.apify.com/?tools=jumblemog/project-scheduler`. The agent writes the tasks as short lines like `Backend: 8-12d after Design; needs 2 developers`, then gives the user the dates, the critical path and the links to the Gantt chart and files.

### FAQ

**Can I open the schedule in MS Project or Excel?** Yes. Each run gives MS Project XML (MSPDI), an Excel workbook with the schedule and a Gantt sheet, and a CSV.

**Does it skip weekends and holidays?** Yes. It counts over your working week and skips public holidays for the country or region you give (`US`, `GB-SCT`, `DE-BY`), plus your own closed days.

**What if one person is booked on two tasks at once?** Resource leveling moves tasks so nobody is booked twice, and finds the earliest finish date that allows.

**Can I add the schedule to Google Calendar?** Yes, from the iCalendar file. There is also a Mermaid version for GitHub and Notion.

**Does it work with Zapier, Make or n8n?** Yes. Call the Actor with its Apify app or an HTTP request and send the Gantt chart or the files on.

### Input

| Field | What it is |
|---|---|
| `title` | The project's name. |
| `tasks` | Task lines (or JSON objects) as above. |
| `tasksCsv` | A task table: CSV/TSV/JSON text, or a link to a CSV, JSON or Excel file or a public Google Sheet. |
| `resources`, `resourcesCsv` | People, teams and equipment as lines or a table (Name, Capacity, Rate, Days off). |
| `startDate` | The first day (YYYY-MM-DD); empty means the first working day after today. |
| `workDays`, `hoursPerDay`, `dayStarts` | The working week (default Mon-Fri), hours per day (8) and start time (09:00). |
| `holidays` | Country or region codes, dates, ranges and named closures. |
| `deadline` | The date the project must finish by. |
| `leveling` | `auto` (default): level only when someone is overbooked. `off`: dependencies only, overbooking reported. |
| `uncertaintyPercent` | A range for tasks without one: best case this much shorter, worst case twice this much longer. |
| `simulations` | Monte Carlo runs (default 2,000; 0 turns the simulation off). |
| `currency` | A currency code for costs; empty takes it from the rates. |
| `timeLimitSeconds` | Time for leveling (default 10, up to 300). |
| `jobs` | Several projects in one run. |

### Output

One dataset item per project, and every project in the run's `OUTPUT` record (`schedulesMade`, `schedulesFailed`, `schedules`). Shortened:

```json
{
  "title": "Website launch",
  "status": "scheduled",
  "summary": "Website launch: 7 tasks from 2026-11-02 to 2026-12-14 (29 working days); 6 on the critical path, leveled for Developers; meets the 2026-12-18 deadline; 80% chance of finishing by 2026-12-15.",
  "start": "2026-11-02", "finish": "2026-12-14", "workingDays": 29, "calendarDays": 43,
  "deadline": "2026-12-18", "deadlineMet": true, "deadlineSlackDays": 4,
  "leveled": true, "levelingAddedDays": 6, "levelingOptimal": true,
  "criticalPath": ["Requirements", "Design", "Frontend", "Backend", "Testing", "Go live"],
  "finishOdds": {"runs": 2000, "p50": "2026-12-14", "p80": "2026-12-15", "p90": "2026-12-16", "p95": "2026-12-16",
                 "plannedFinishChancePercent": 51.3, "deadlineChancePercent": 100.0},
  "totalCost": 29600, "currency": "USD",
  "tasks": [{"id": "3", "wbs": "2.1", "name": "Backend", "phase": "Build", "start": "2026-11-23", "finish": "2026-12-07",
             "durationDays": 10, "critical": true, "totalFloatDays": 0, "freeFloatDays": 0, "levelingDelayDays": 6,
             "predecessors": "2", "assignedTo": ["Developers 1", "Developers 2"], "criticalityPercent": 100, "cost": 15200}],
  "phases": [{"wbs": "2", "name": "Build", "start": "2026-11-13", "finish": "2026-12-07", "workingDays": 16}],
  "milestones": [{"name": "Go live", "date": "2026-12-14", "critical": true}],
  "resources": [{"name": "Developers", "capacity": 2, "workDays": 26, "utilizationPercent": 44.8, "cost": 19760,
                 "overbookedBeforeLeveling": true}],
  "mermaid": "gantt\n    title Website launch\n    dateFormat YYYY-MM-DD\n ...",
  "imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/schedule-001-Website-launch.png",
  "pdfUrl": "...", "xlsxUrl": "...", "msProjectUrl": "...", "csvUrl": "...", "icsUrl": "...", "mermaidUrl": "...",
  "warnings": [], "notes": []
}
```

Each task also has `startTime` and `finishTime` (with hours), `earlyStart`, `earlyFinish`, `lateStart`, `lateFinish`, `resources` with units, `optimisticDays` and `pessimisticDays`, `notBefore`, `deadline` and `deadlineMet`. `report` holds the whole schedule as Markdown. Projects that can't be scheduled come back with `status: "failed"` and an `error` that says what to change (such as the tasks in a dependency loop).

### How it works

1. **Read**: tasks, links, resources and dates are read from text, JSON or tables; names are matched without regard to case, and phases are expanded to their first or last tasks.
2. **Calendar**: time is counted in working minutes from the start date, skipping non-working weekdays, holidays and closures, so durations and lags are always working time.
3. **Critical path**: a forward pass gives each task's earliest dates, a backward pass from the finish (or an earlier deadline) gives the latest, and float is the difference.
4. **Leveling**: if a person or team is overbooked, the schedule becomes a resource-constrained scheduling problem for CP-SAT: interval variables for tasks, cumulative constraints for each resource with days off as blocked time, and the links as precedence constraints. It first minimizes the finish date (starting from a quick priority-rule schedule), then, keeping that date, moves every task as early as possible. Float is then recomputed with each person's order of work, so the critical path reflects resources too.
5. **Simulation**: task durations are drawn from PERT distributions over their ranges, with the links and each person's order of work kept, and the finish dates of all runs give the odds.
6. **Files**: the Gantt chart is drawn as SVG and turned into PNG and PDF; Excel, MS Project XML, CSV, iCalendar, Mermaid and Markdown are written from the same result.

### Checks

The build runs three sample projects end to end (a leveled website launch, a house build from a table, and a dependency loop that must fail with its reason), and over 30 unit tests cover the reader, the calendar, CPM floats against a textbook case, every link type, leveling with independent checks that no resource is overbooked and no link is broken, days off, the simulation and every file.

### Pricing

You pay per **schedule made**, at the price shown on this page, with every file included. Projects that can't be scheduled are never charged. Leveling past the first 30 seconds of a project adds a small **leveling time** charge per started 10 seconds, so with the default 10-second time limit it never applies. If you set a maximum cost per run, the Actor checks before each project that it fits and shortens the time limit to stay within it.

### Speed

Most projects (up to a hundred or so tasks) are scheduled in well under a second, and a run through the API answers in about 10 to 15 seconds, including the cloud container starting and the files being saved. Leveling large projects takes longer: in tests, 400 random tasks shared by five people and teams were leveled, and proven the shortest possible, in about 4 seconds.

### Tips and limits

- **Fixed durations**: a task takes the same time whoever does it; adding people to a team doesn't shorten a task. Split work into smaller tasks to let it run in parallel.
- **Days off**: a task doesn't stop for someone's days off; leveling schedules it before or after them. Days off for a team take the whole team away on those days, so list a person separately to take just them away.
- **Working time only**: durations and lags count working time. For waiting time that runs over weekends (concrete curing), use a lag in working days that covers it.
- **Months** are 4 working weeks, and weeks are your working days per week.
- **Constraints**: "not before" dates and deadlines; there are no "must start on" constraints and no progress tracking with actual dates yet.
- **The simulation** keeps each person's leveled order of work, and tasks without ranges stay fixed unless you set `uncertaintyPercent`.
- **MS Project XML**: tasks the leveling moved carry a "start no earlier than" date, so MS Project, ProjectLibre or GanttProject open the leveled plan as it is here rather than leveling again.
- **Sizes**: up to 1,500 tasks and 300 resources per project and 50 projects per run. The PNG shows the first 60 rows; the PDF, Excel and CSV have them all.

### For AI agents and developers

This Actor works with the [Apify MCP server](https://mcp.apify.com), so AI agents can call it as a tool: send the tasks as text lines and read `summary`, `criticalPath`, `tasks`, `finishOdds`, `mermaid` and the file links from the result. Unknown words in a task line come back as an error that shows the accepted forms, so an agent can correct itself. A direct API call that waits for the answer:

```bash
curl -X POST "https://api.apify.com/v2/acts/jumblemog~project-scheduler/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tasks": ["Design: 5d", "Build: 10d after Design; needs 2 developers", "Test: 3-6d after Build"], "resources": ["Developers: 2"], "holidays": ["US"]}'
```

`run-sync` instead returns the run's `OUTPUT`: the status, how many schedules were made and every schedule as JSON.

In Python, with the `apify-client` package:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("jumblemog/project-scheduler").call(run_input={
    "tasks": ["Design: 5d", "Build: 10d after Design; needs 2 developers", "Test: 3-6d after Build"],
    "resources": ["Developers: 2"], "startDate": "2026-11-02", "holidays": ["US"], "deadline": "2026-12-04",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["summary"], item["criticalPath"], item["finishOdds"])
```

### Credits

- Scheduling: [Google OR-Tools](https://developers.google.com/optimization) CP-SAT (Apache License 2.0) and [NumPy](https://numpy.org) (BSD-3-Clause) for the simulation.
- Holidays: the [holidays](https://github.com/vacanza/holidays) library (MIT).
- Files: [openpyxl](https://openpyxl.readthedocs.io) (MIT) for Excel, [vl-convert](https://github.com/vega/vl-convert) (BSD-3-Clause) for the PNG and PDF pages, [pypdf](https://github.com/py-pdf/pypdf) (BSD-3-Clause) to join them, and the Inter font (SIL Open Font License), subset with [fontTools](https://github.com/fonttools/fonttools) (MIT).

# Actor input Schema

## `title` (type: `string`):

Shown on the Gantt chart and in the file names.

## `tasks` (type: `array`):

One task per line: 'Name: duration; links; people'. Durations: 5d, 4h, 2w, 1mo, 3-5d (a range: plans on the middle, simulates the spread), 3/5/10d (best/likely/worst) or milestone. Links: 'after A, B', 'after A +2d' (lag), 'starts with A', 'finishes with A', 'SS A', '3FS+1d' (task 3). People: 'by Dana', 'needs 2 developers', 'uses crane'. Dates: 'not before 2026-11-09', 'deadline 2026-12-04'. Costs: '$500'. Headings ('## Build' or 'Build:') group tasks into phases; 'after Build' waits for the phase's last tasks. JSON objects work too.

## `tasksCsv` (type: `string`):

Instead of or as well as the lines above: a table pasted as CSV/TSV/JSON, or a link to a CSV, JSON or Excel file or a public Google Sheet. Columns (any order, common names understood): ID, Task, Duration, Predecessors ('2FS+3d, 4SS' or task names), Resources ('Dana, Developer\[200%]'), Optimistic/Likely/Pessimistic, Start no earlier than, Deadline, Phase, Milestone, Cost, Notes. Exports from MS Project, Smartsheet, Asana or Jira work after renaming columns if needed.

## `resources` (type: `array`):

One per line: 'Name', 'Developers: 3' (a team of 3), '$95/h' or '$600/day' rates, and 'off 2026-12-21 to 2026-12-31' days off. People named on tasks but not listed are assumed to be one person (or as many as one task needs).

## `resourcesCsv` (type: `string`):

A resource table: Name, Capacity (team size), Rate (per hour), Days off.

## `startDate` (type: `string`):

The project's first day (YYYY-MM-DD). It moves to the next working day if needed. Empty: the first working day after today.

## `workDays` (type: `string`):

Mon-Fri, Sun-Thu, Mon-Sat, 'Mon, Tue, Thu' or every day.

## `hoursPerDay` (type: `number`):

How many hours a day count as one day of work (1d = this many hours).

## `dayStarts` (type: `string`):

The time of day work starts, for start and finish times.

## `holidays` (type: `array`):

Country codes for public holidays (US, GB, CA, AU, DE, FR, IN and 100 more; regions like US-CA, GB-SCT, DE-BY), dates (2026-12-24), ranges (2026-12-24 to 2027-01-01) and named days ('Company offsite: 2026-11-20').

## `deadline` (type: `string`):

The date the project must finish by (YYYY-MM-DD). The result says whether it's met, by how many working days, and the chance of meeting it when tasks have ranges.

## `leveling` (type: `string`):

Auto levels only when someone is overbooked, with OR-Tools CP-SAT: the earliest finish date first, then every task as early as it can go.

## `uncertaintyPercent` (type: `integer`):

Gives every task without its own range one: best case this much shorter, worst case twice this much longer (tasks overrun more often than they finish early). 0: only tasks with ranges vary.

## `simulations` (type: `integer`):

How many simulated runs give the finish-date odds (P50, P80, P90, P95) and each task's chance of being critical, when tasks have ranges. 0 turns the simulation off.

## `currency` (type: `string`):

A currency code for costs (USD, EUR, GBP). Empty: taken from the rates, such as $ or €.

## `timeLimitSeconds` (type: `integer`):

How long the leveling may search. Projects with a few hundred tasks level in seconds; leveling past 30 seconds is billed per started 10 seconds.

## `jobs` (type: `array`):

Schedule several projects in one run: a list of objects with their own tasks, resources or settings (any field above); fields not given come from above: \[{"title": "Office move", "tasks": \[...]}, {"title": "Audit", "tasks": \[...], "deadline": "2026-12-11"}].

## Actor input object example

```json
{
  "title": "Website launch",
  "tasks": [
    "## Plan",
    "Requirements: 3d; by Dana",
    "Design: 5d after Requirements; by Dana",
    "## Build",
    "Backend: 8-12d after Design; needs 2 developers",
    "Frontend: 6d after Design; needs 1 developer",
    "Content: 4d, starts with Frontend; by Lee",
    "## Launch",
    "Testing: 5d after Backend, Frontend; by QA",
    "Go live: milestone after Testing"
  ],
  "resources": [
    "Developers: 2, $95/h",
    "Dana: $80/h, off 2026-11-23 to 2026-11-27",
    "Lee: $60/h",
    "QA: $70/h"
  ],
  "startDate": "2026-11-02",
  "workDays": "Mon-Fri",
  "hoursPerDay": 8,
  "dayStarts": "09:00",
  "holidays": [
    "US"
  ],
  "deadline": "2026-12-18",
  "leveling": "auto",
  "uncertaintyPercent": 0,
  "simulations": 2000,
  "timeLimitSeconds": 10
}
```

# Actor output Schema

## `results` (type: `string`):

One row per project: the Gantt chart, the summary and the files.

## `tasks` (type: `string`):

Every task with its dates and float.

## `resources` (type: `string`):

Each resource's work and cost.

## `output` (type: `string`):

The OUTPUT record: every schedule in one JSON object.

## `files` (type: `string`):

The Gantt charts, PDFs, Excel, MS Project XML, CSV, iCalendar, Mermaid, Markdown and JSON files.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "title": "Website launch",
    "tasks": [
        "## Plan",
        "Requirements: 3d; by Dana",
        "Design: 5d after Requirements; by Dana",
        "## Build",
        "Backend: 8-12d after Design; needs 2 developers",
        "Frontend: 6d after Design; needs 1 developer",
        "Content: 4d, starts with Frontend; by Lee",
        "## Launch",
        "Testing: 5d after Backend, Frontend; by QA",
        "Go live: milestone after Testing"
    ],
    "resources": [
        "Developers: 2, $95/h",
        "Dana: $80/h, off 2026-11-23 to 2026-11-27",
        "Lee: $60/h",
        "QA: $70/h"
    ],
    "startDate": "2026-11-02",
    "holidays": [
        "US"
    ],
    "deadline": "2026-12-18"
};

// Run the Actor and wait for it to finish
const run = await client.actor("jumblemog/project-scheduler").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "title": "Website launch",
    "tasks": [
        "## Plan",
        "Requirements: 3d; by Dana",
        "Design: 5d after Requirements; by Dana",
        "## Build",
        "Backend: 8-12d after Design; needs 2 developers",
        "Frontend: 6d after Design; needs 1 developer",
        "Content: 4d, starts with Frontend; by Lee",
        "## Launch",
        "Testing: 5d after Backend, Frontend; by QA",
        "Go live: milestone after Testing",
    ],
    "resources": [
        "Developers: 2, $95/h",
        "Dana: $80/h, off 2026-11-23 to 2026-11-27",
        "Lee: $60/h",
        "QA: $70/h",
    ],
    "startDate": "2026-11-02",
    "holidays": ["US"],
    "deadline": "2026-12-18",
}

# Run the Actor and wait for it to finish
run = client.actor("jumblemog/project-scheduler").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "title": "Website launch",
  "tasks": [
    "## Plan",
    "Requirements: 3d; by Dana",
    "Design: 5d after Requirements; by Dana",
    "## Build",
    "Backend: 8-12d after Design; needs 2 developers",
    "Frontend: 6d after Design; needs 1 developer",
    "Content: 4d, starts with Frontend; by Lee",
    "## Launch",
    "Testing: 5d after Backend, Frontend; by QA",
    "Go live: milestone after Testing"
  ],
  "resources": [
    "Developers: 2, $95/h",
    "Dana: $80/h, off 2026-11-23 to 2026-11-27",
    "Lee: $60/h",
    "QA: $70/h"
  ],
  "startDate": "2026-11-02",
  "holidays": [
    "US"
  ],
  "deadline": "2026-12-18"
}' |
apify call jumblemog/project-scheduler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jumblemog/project-scheduler"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/VQa7ChnS0k03EcfGG/builds/q90LJFBtONOxxA9YT/openapi.json
