tickstem/cron
April 21, 2026 · View on GitHub
Go SDK for Tickstem — reliable cron jobs for production apps.
Works on Vercel, Railway, Render, Fly.io, and anywhere else that can't run an always-on scheduler.
Install
go get github.com/tickstem/cron
Quick start
package main
import (
"context"
"log"
"os"
"github.com/tickstem/cron"
)
func main() {
client := cron.New(os.Getenv("TICKSTEM_API_KEY"))
job, err := client.Register(context.Background(), cron.RegisterParams{
Name: "daily-cleanup",
Schedule: "0 2 * * *",
Endpoint: "https://yourapp.com/jobs/cleanup",
})
if err != nil {
log.Fatal(err)
}
log.Printf("job registered: %s (next run: %s)", job.ID, job.NextRunAt)
}
Get your API key at app.tickstem.dev.
Usage
Create a client
// Minimal — uses https://api.tickstem.dev/v1
client := cron.New(os.Getenv("TICKSTEM_API_KEY"))
// With options
client := cron.New(apiKey,
cron.WithBaseURL("http://localhost:8080/v1"), // local dev / tsk-local
)
Register a job
job, err := client.Register(ctx, cron.RegisterParams{
Name: "send-digest",
Description: "Weekly email digest to all users",
Schedule: "0 9 * * 1", // every Monday at 09:00
Endpoint: "https://yourapp.com/jobs/digest",
Method: "POST", // default
TimeoutSecs: 60, // default: 30
})
List jobs
jobs, err := client.List(ctx)
for _, j := range jobs {
fmt.Printf("%s %s %s\n", j.ID, j.Schedule, j.Status)
}
Get a job
job, err := client.Get(ctx, "job_abc123")
Pause / Resume
job, err := client.Pause(ctx, jobID)
job, err := client.Resume(ctx, jobID)
Delete a job
err := client.Delete(ctx, jobID)
Execution history
executions, err := client.Executions(ctx, jobID)
for _, e := range executions {
fmt.Printf("%s %s %dms\n", e.ID, e.Status, *e.DurationMs)
}
Securing your endpoint
Tickstem calls your endpoint over the public internet. To prevent unauthorized callers from triggering your job handler, add a shared secret in the job headers and validate it in your handler:
job, err := client.Register(ctx, cron.RegisterParams{
Name: "daily-cleanup",
Schedule: "0 2 * * *",
Endpoint: "https://yourapp.com/jobs/cleanup",
Method: "POST",
Headers: map[string]string{
"X-Tickstem-Secret": os.Getenv("CRON_SECRET"),
},
})
In your handler, reject requests that don't carry the secret:
func cronHandler(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Tickstem-Secret") != os.Getenv("CRON_SECRET") {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
// ... do work
}
Use a long random value for CRON_SECRET (e.g. openssl rand -hex 32).
Store it as an environment variable in both your app and your Tickstem job — never hardcode it.
Error handling
job, err := client.Get(ctx, someID)
if err != nil {
if cron.IsNotFound(err) {
// job doesn't exist
}
if cron.IsUnauthorized(err) {
// invalid API key
}
if cron.IsQuotaExceeded(err) {
// monthly execution quota hit — upgrade at app.tickstem.dev/dashboard/billing
}
// inspect the raw error
var apiErr *cron.APIError
if errors.As(err, &apiErr) {
fmt.Println(apiErr.StatusCode, apiErr.Message)
}
}
Local development
Use tsk-local to test your job handlers without hitting the live API.
It runs the full Tickstem API contract in-memory on your machine.
go install github.com/tickstem/cron/cmd/tsk-local@latest
tsk-local # starts on :8090
tsk-local --port 9000 # custom port
Then point the SDK at it:
client := cron.New("any-key",
cron.WithBaseURL("http://localhost:8090/v1"))
The dashboard at http://localhost:8090 shows registered jobs,
execution history, and a run button for instant manual triggers.
Jobs and history are in-memory — everything resets on restart. Use the real platform for production.
Cron expression reference
┌─────── minute (0–59)
│ ┌───── hour (0–23)
│ │ ┌─── day of month (1–31)
│ │ │ ┌─ month (1–12)
│ │ │ │ ┌ day of week (0–6, Sun=0)
│ │ │ │ │
* * * * *
Examples:
0 * * * * every hour
*/15 * * * * every 15 minutes
0 2 * * * daily at 02:00 UTC
0 9 * * 1 every Monday at 09:00 UTC
0 0 1 * * first day of every month
Use crontab.guru to build and validate expressions.
Pricing
| Plan | Executions/month | Price |
|---|---|---|
| Free | 1,000 | $0 |
| Starter | 10,000 | $12/mo |
| Pro | 100,000 | $29/mo |
License
MIT