Skip to content
tutorial

Parse and validate cron expressions via REST API

| 4 min read
Clock gears and scheduling calendar
Photo by Lukas Blazek on Unsplash

You're building an admin panel where users schedule recurring jobs. They type a cron expression into a text field, hit save, and expect the system to do the right thing. The problem: cron syntax is cryptic. */15 * * * * is clear enough, but what about 0 9 1-15 * 1-5? Most users (and many developers) can't read that at a glance.

You need to show users what their expression means *before* they save it. A description like "At 09:00 on days 1 through 15, Monday through Friday" is worth more than a tooltip linking to crontab.guru.

Botoi's cron parser API does three things in a single POST request: validates the expression, generates a human-readable description, and returns the next scheduled run times. No cron libraries to install, no parsing logic to maintain.

The parse endpoint

Send a cron expression to /v1/cron/parse and get back a structured breakdown.

curl -X POST https://api.botoi.com/v1/cron/parse \
  -H "Content-Type: application/json" \
  -d '{"expression": "*/15 * * * *"}'

Response:

{
  "success": true,
  "data": {
    "isValid": true,
    "description": "Every 15 minutes",
    "nextRuns": [
      "2026-03-26T18:30:00Z",
      "2026-03-26T18:45:00Z",
      "2026-03-26T19:00:00Z",
      "2026-03-26T19:15:00Z",
      "2026-03-26T19:30:00Z"
    ],
    "parts": {
      "minute": "*/15",
      "hour": "*",
      "dayOfMonth": "*",
      "month": "*",
      "dayOfWeek": "*"
    }
  }
}
Clock face showing precise time
Photo by Lukas Blazek on Unsplash

The response includes everything you need: an isValid flag, a plain-English description, the next five run times in UTC, and the individual parts broken out by field. You can display the description directly in your UI and use the nextRuns array to show a preview of upcoming executions.

Get more upcoming runs

If you need more than five preview dates, or you only care about the schedule and not the description, use the /v1/cron/next endpoint. Pass a count parameter to control how many future run times you get back.

curl -X POST https://api.botoi.com/v1/cron/next \
  -H "Content-Type: application/json" \
  -d '{"expression": "0 9 * * MON-FRI", "count": 5}'

Response:

{
  "success": true,
  "data": {
    "nextRuns": [
      "2026-03-27T09:00:00Z",
      "2026-03-30T09:00:00Z",
      "2026-03-31T09:00:00Z",
      "2026-04-01T09:00:00Z",
      "2026-04-02T09:00:00Z"
    ]
  }
}

Notice the gap between March 27 (Friday) and March 30 (Monday). The expression 0 9 * * MON-FRI skips weekends, and the API reflects that correctly.

Build a cron preview widget

Here's how to wire the parse endpoint into a frontend input field. As the user types a cron expression, the widget calls the API and shows the description and upcoming runs in real time.

async function parseCron(expression) {
  const res = await fetch("https://api.botoi.com/v1/cron/parse", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ expression }),
  });
  return res.json();
}

// Example: user types "0 9 * * MON-FRI" into a text input
const input = document.querySelector("#cron-input");
const preview = document.querySelector("#cron-preview");

input.addEventListener("input", async (e) => {
  const { data } = await parseCron(e.target.value);

  if (data.isValid) {
    preview.innerHTML = `
      <p class="text-green-600">\${data.description}</p>
      <ul>
        \${data.nextRuns
          .map((run) => `<li>\${new Date(run).toLocaleString()}</li>`)
          .join("")}
      </ul>
    `;
  } else {
    preview.innerHTML = '<p class="text-red-600">Invalid expression</p>';
  }
});

For production use, add a debounce (200-300ms) so you don't fire a request on every keystroke. The API responds in under 50ms from Cloudflare's edge, so the preview feels instant once the request goes out.

React / Preact version

If you're using React or Preact, here's a component that wraps the same logic with an AbortController to cancel stale requests.

import { useState, useEffect } from "react";

function CronPreview({ value }) {
  const [result, setResult] = useState(null);

  useEffect(() => {
    if (!value.trim()) {
      setResult(null);
      return;
    }

    const controller = new AbortController();
    fetch("https://api.botoi.com/v1/cron/parse", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ expression: value }),
      signal: controller.signal,
    })
      .then((r) => r.json())
      .then(({ data }) => setResult(data))
      .catch(() => {});

    return () => controller.abort();
  }, [value]);

  if (!result) return null;

  if (!result.isValid) {
    return <p className="text-red-600">Invalid expression</p>;
  }

  return (
    <div>
      <p className="font-medium">{result.description}</p>
      <p className="text-sm text-gray-500 mt-1">
        Next run: {new Date(result.nextRuns[0]).toLocaleString()}
      </p>
    </div>
  );
}

Validate user input before saving

Client-side previews are great for UX, but you should also validate on the server before persisting a cron job. Call the parse endpoint from your backend and check the isValid flag.

async function saveCronJob(name, expression) {
  // Validate the expression before saving
  const res = await fetch("https://api.botoi.com/v1/cron/parse", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ expression }),
  });
  const { data } = await res.json();

  if (!data.isValid) {
    throw new Error("Invalid cron expression: " + expression);
  }

  // Save to your database with the parsed metadata
  await db.cronJobs.create({
    name,
    expression,
    description: data.description,
    nextRun: data.nextRuns[0],
  });

  return { description: data.description, nextRun: data.nextRuns[0] };
}

When a user submits an invalid expression, the API returns a clean response you can act on:

{
  "success": true,
  "data": {
    "isValid": false,
    "description": null,
    "nextRuns": [],
    "parts": null
  }
}

No exceptions to catch, no regex to write. Check data.isValid and return an error message to the user. You can also store the description and nextRun alongside the raw expression in your database, so you can display them in listing views without calling the API again.

Common cron expressions and what the API returns

Expression Description
* * * * * Every minute
*/15 * * * * Every 15 minutes
0 * * * * Every hour
0 9 * * MON-FRI At 09:00 on Monday through Friday
0 0 1 * * At midnight on the 1st of every month
30 4 * * SUN At 04:30 on Sunday
0 9,17 * * * At 09:00 and 17:00 every day
0 0 * * 0 At midnight on Sunday

Each of these expressions returns the same structured response: validity flag, description, next runs, and parsed parts. You can use the table above as test cases when integrating the API.

Why offload cron parsing to an API?

  • No library dependency. Cron parsing libraries exist for every language, but they add bundle size (frontend) or dependency maintenance (backend). A single HTTP call replaces the library.
  • Consistent behavior across services. If your system has a Node.js scheduler, a Python worker, and a Go microservice, they all parse cron expressions differently. One API gives you a single source of truth.
  • Human-readable descriptions for free. Generating natural language from cron syntax is harder than parsing the schedule. The API handles both in one call.
  • Instant preview for users. Sub-50ms edge responses make real-time validation practical, even on every keystroke with debouncing.

Frequently asked questions

Does the cron parser API support non-standard expressions like @daily or @weekly?
Yes. The parse endpoint accepts both standard five-field cron expressions and common shorthand aliases like @yearly, @monthly, @weekly, @daily, and @hourly. The response normalizes them into the standard five-field format.
What timezone are the nextRuns timestamps in?
All timestamps in the response are in UTC (ISO 8601 format). Convert them to the user's local timezone on the client side using Intl.DateTimeFormat or a library like date-fns.
Is there a limit on how many next runs I can request?
The /v1/cron/next endpoint accepts a count parameter. You can request up to 100 upcoming run times in a single call. The default is 5 if you omit the parameter.
Do I need an API key to use the cron parser?
No. Anonymous access is available at 5 requests per minute with IP-based rate limiting. For higher throughput, sign up for an API key at botoi.com/api.
Can I use day names like MON-FRI in cron expressions?
Yes. The parser supports three-letter day abbreviations (SUN, MON, TUE, WED, THU, FRI, SAT) and month abbreviations (JAN through DEC) in the appropriate fields.

Try this API

Cron Parser API — interactive playground and code examples

More tutorial posts

Start building with botoi

150+ API endpoints for lookup, text processing, image generation, and developer utilities. Free tier, no credit card.