Documentation

Quickstart

Get your first cron job running in under five minutes.

1. Install the SDK

go get github.com/tickstem/cron

2. Get an API key

Sign in at app.tickstem.dev, go to API Keys, and create a key. Store it as an environment variable:

export TICKSTEM_API_KEY=tsk_live_...

3. Register your first job

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 * * *",   // every day at 02:00 UTC
        Endpoint: "https://yourapp.com/jobs/cleanup",
    })
    if err != nil {
        log.Fatal(err)
    }

    log.Printf("registered: %s  next run: %s", job.ID, job.NextRunAt)
}

Tickstem will call POST https://yourapp.com/jobs/cleanup every day at 02:00 UTC. Your endpoint just needs to return a 2xx status — no SDK required on the receiving side.

Idempotency tip: Design your job handler to be safe to run more than once. Tickstem retries on network errors, so your handler may occasionally be called twice for the same scheduled time.

Core concepts

Jobs

A Job is a named schedule that calls an HTTP endpoint. Jobs are identified by a stable id (e.g. job_abc123). You can register the same job on every deploy — if a job with the same name already exists for your account, Register upserts it.

Executions

Each time a job fires, an Execution is created. It records the outcome (success, failed, timeout), HTTP status code, duration, and any error message. History is retained for 7 days on the Free plan, 30 days on Starter, and 90 days on Pro and Business.

Schedules

Schedules use standard 5-field cron syntax in UTC. See the cron reference for examples.

Installation

go get github.com/tickstem/cron

Requires Go 1.21 or later. The SDK has no third-party dependencies.

Client

// Minimal — points at https://api.tickstem.dev/v1
client := cron.New(os.Getenv("TICKSTEM_API_KEY"))

// With options
client := cron.New(apiKey,
    cron.WithBaseURL("http://localhost:8090/v1"), // local dev
)

The client is safe for concurrent use and should be created once and reused.

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 UTC
    Endpoint:    "https://yourapp.com/jobs/digest",
    Method:      "POST",                                // default
    Headers: map[string]string{
        "X-Tickstem-Secret": os.Getenv("CRON_SECRET"), // see Securing your endpoint
    },
    TimeoutSecs: 60,                                    // default: 30
})

Parameters

FieldTypeDescription
Name*stringHuman-readable label. Used as the upsert key — registering the same name updates the existing job.
Schedule*string5-field cron expression in UTC.
Endpoint*stringHTTPS URL to call on each execution.
DescriptionstringOptional note shown in the dashboard.
MethodstringHTTP method. Defaults to POST.
Headersmap[string]stringExtra headers sent with each request. Use for shared secrets.
TimeoutSecsintSeconds before the execution is marked timeout. Defaults to 30.

List & Get

// list all jobs
jobs, err := client.List(ctx)
for _, j := range jobs {
    fmt.Printf("%s  %s  next: %s\n", j.ID, j.Schedule, j.NextRunAt)
}

// get a single job
job, err := client.Get(ctx, "job_abc123")

Pause & Resume

job, err := client.Pause(ctx, jobID)
job, err := client.Resume(ctx, jobID)

A paused job is not executed but retains its schedule. Resume it to re-enable execution from the next scheduled time.

Delete

err := client.Delete(ctx, jobID)

Permanently removes the job and all its execution history.

Execution history

executions, err := client.Executions(ctx, jobID)
for _, e := range executions {
    fmt.Printf("%s  status=%s  duration=%dms\n",
        e.ScheduledAt.Format(time.RFC3339),
        e.Status,
        *e.DurationMs,
    )
}

Returns executions in reverse chronological order. Each Execution includes:

  • Statussuccess, failed, timeout, running, or pending
  • StatusCode — HTTP response code from your endpoint
  • DurationMs — wall-clock time of the HTTP call
  • Error — error message when status is failed or timeout
  • ScheduledAt, StartedAt, FinishedAt — timing breakdown

Error handling

job, err := client.Get(ctx, someID)
if err != nil {
    if cron.IsNotFound(err) {
        // job doesn't exist
    }
    if cron.IsUnauthorized(err) {
        // invalid or revoked API key
    }
    if cron.IsQuotaExceeded(err) {
        // monthly execution limit reached
        // upgrade at app.tickstem.dev/dashboard/billing
    }

    // inspect the raw API error
    var apiErr *cron.APIError
    if errors.As(err, &apiErr) {
        fmt.Println(apiErr.StatusCode, apiErr.Message)
    }
}

Securing your endpoint

Tickstem calls your endpoint over the public internet. Any caller that knows your URL can trigger it. Add a shared secret to reject unauthorized requests.

1. Generate a secret

openssl rand -hex 32

Store the output as CRON_SECRET in both your app's environment and as a job header in Tickstem.

2. Register the job with the secret

job, err := client.Register(ctx, cron.RegisterParams{
    Name:     "daily-cleanup",
    Schedule: "0 2 * * *",
    Endpoint: "https://yourapp.com/jobs/cleanup",
    Headers: map[string]string{
        "X-Tickstem-Secret": os.Getenv("CRON_SECRET"),
    },
})

3. Validate in your handler

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
}
Never hardcode the secret. Use environment variables. Rotate it by updating both your app's env and the job's headers in the dashboard or via the API.

Local dev with tsk-local

tsk-local runs the full Tickstem API contract in-memory on your machine so you can test job handlers without touching the live API or needing real credentials.

Install

go install github.com/tickstem/cron/cmd/tsk-local@latest

Run

tsk-local              # starts on :8090
tsk-local --port 9000  # custom port

Point the SDK at it

client := cron.New("any-key",
    cron.WithBaseURL("http://localhost:8090/v1"))

The local 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

Schedules use standard 5-field cron syntax. All times are UTC.

┌───────── minute      (0–59)
│ ┌─────── hour        (0–23)
│ │ ┌───── day of month (1–31)
│ │ │ ┌─── month       (1–12)
│ │ │ │ ┌─ day of week  (0–6, Sun=0)
│ │ │ │ │
* * * * *
ExpressionMeaning
* * * * *Every minute
*/15 * * * *Every 15 minutes
0 * * * *Every hour at :00
0 2 * * *Daily at 02:00 UTC
0 9 * * 1Every Monday at 09:00 UTC
0 0 1 * *First day of every month
0 0 * * 0Every Sunday at midnight

Use crontab.guru to build and validate expressions.

Email Verification

The Tickstem email verification API checks whether an email address is deliverable before you store it. It runs four checks in sequence:

  • Syntax — RFC 5322 parse. Catches user@@example, missing TLD, etc.
  • MX lookup — confirms the domain has mail exchange records. Rejects [email protected] without a network call to the mail server.
  • Disposable detection — flags addresses on known throwaway services (Mailinator, Guerrilla Mail, and 200+ others).
  • Role-based detection — flags generic inboxes (admin@, info@, noreply@) that are unlikely to belong to a real person.

The API returns a structured result for every check. You decide what to do with it — block, warn, or accept.

Privacy note: Tickstem does not probe the mail server (no SMTP). The email address is stored in your verification history but never sent to a third party.

Installation

go get github.com/tickstem/verify

Requires Go 1.22 or later. No third-party runtime dependencies.

Same API key as the cron SDK — no additional credentials needed.

Verify an email

import "github.com/tickstem/verify"

client := verify.New(os.Getenv("TICKSTEM_API_KEY"))

result, err := client.Verify(ctx, "[email protected]")
if err != nil {
    log.Fatal(err)
}

fmt.Println(result.Valid)      // true if safe to store
fmt.Println(result.Disposable) // true if throwaway address
fmt.Println(result.RoleBased)  // true if generic inbox
fmt.Println(result.MXFound)    // true if domain accepts mail
fmt.Println(result.Reason)     // human-readable explanation when invalid

Result fields

FieldTypeDescription
ValidboolTrue only when syntax is correct, MX records exist, and the domain is not disposable.
MXFoundboolTrue if the domain has at least one MX record.
DisposableboolTrue if the domain is a known throwaway service.
RoleBasedboolTrue if the local part matches a generic inbox prefix. Does not affect Valid.
ReasonstringHuman-readable explanation when Valid is false.

Common pattern: block disposable, warn on role-based

result, err := client.Verify(ctx, email)
if err != nil {
    // network or quota error — fail open or closed depending on your policy
    return err
}

if !result.Valid {
    return fmt.Errorf("email address not accepted: %s", result.Reason)
}

if result.RoleBased {
    // warn but don't block — some teams share an inbox
    log.Printf("role-based address submitted: %s", email)
}

History

Every verification is stored in your account history. Retrieve it programmatically to audit, export, or build analytics on top of.

page, err := client.ListHistory(ctx, verify.ListHistoryParams{
    Limit:  50,
    Offset: 0,
})
if err != nil {
    log.Fatal(err)
}

for _, v := range page.Verifications {
    fmt.Printf("%s  valid=%v  disposable=%v  checked=%s\n",
        v.Email, v.Valid, v.Disposable, v.CreatedAt.Format(time.RFC3339))
}

Error handling

result, err := client.Verify(ctx, email)
if err != nil {
    if verify.IsUnauthorized(err) {
        // invalid or revoked API key
    }
    if verify.IsQuotaExceeded(err) {
        // monthly verification limit reached
        // upgrade at app.tickstem.dev/dashboard/billing
    }

    var apiErr *verify.APIError
    if errors.As(err, &apiErr) {
        fmt.Println(apiErr.StatusCode, apiErr.Message)
    }
}
Fail-open vs fail-closed: If a quota or network error occurs during sign-up, decide in advance whether to block the user (fail-closed) or let them through and verify later (fail-open). Most teams fail open for sign-up and fail closed for transactional email lists.

Uptime Monitoring

Tickstem polls your HTTP endpoints on a configurable interval and alerts you by email when they go down or recover. A monitor is a URL + an interval — Tickstem handles the scheduling, check storage, and notifications.

  • Each check issues a GET request with your configured timeout. 2xx and 3xx responses are recorded as up.
  • 4xx, 5xx, connection timeouts, and DNS failures are recorded as down.
  • After two consecutive failures the monitor flips to failing status and sends an email alert.
  • When the endpoint recovers, you get a recovery notification and the monitor returns to active.
One dashboard, one alert. Because uptime monitoring lives alongside your cron jobs, a failing monitor at 05:58 immediately explains why the cron job that fired at 06:00 failed — without digging through logs.

Create a monitor

From the dashboard: Monitors → New monitor. Or via the REST API:

curl -X POST https://api.tickstem.dev/v1/monitors \
  -H "Authorization: Bearer $TICKSTEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production API",
    "url": "https://api.yourapp.com/health",
    "interval_secs": 60,
    "timeout_secs": 10
  }'

Parameters

FieldTypeDescription
name*stringHuman-readable label shown in the dashboard.
url*stringHTTPS URL to poll.
interval_secsintCheck frequency in seconds. Plan minimum: Free 300, Starter 60, Pro/Business 30. Default: 60.
timeout_secsintSeconds before marking the check as timeout. Range: 5–30. Default: 10.

Monitor status values

StatusMeaning
activeChecks are running normally.
failingTwo or more consecutive failures. Email alert has been sent.
pausedChecks are suspended. No alerts will fire.

Pause, resume, delete

# pause
curl -X PATCH https://api.tickstem.dev/v1/monitors/{id}/pause \
  -H "Authorization: Bearer $TICKSTEM_API_KEY"

# resume
curl -X PATCH https://api.tickstem.dev/v1/monitors/{id}/resume \
  -H "Authorization: Bearer $TICKSTEM_API_KEY"

# delete (removes the monitor and all its check history)
curl -X DELETE https://api.tickstem.dev/v1/monitors/{id} \
  -H "Authorization: Bearer $TICKSTEM_API_KEY"

Check history

curl https://api.tickstem.dev/v1/monitors/{id}/checks \
  -H "Authorization: Bearer $TICKSTEM_API_KEY"

Returns checks in reverse chronological order (most recent first). Each check includes:

  • statusup, down, or timeout
  • status_code — HTTP response code from your endpoint, if a response was received
  • duration_ms — wall-clock time of the HTTP request
  • error — error message for down and timeout checks
  • checked_at — RFC 3339 timestamp of when the check ran

Check history is retained for 7 days on Free, 30 days on Starter, and 90 days on Pro and Business — the same retention policy as cron execution logs.

Error handling

The REST API returns standard error shapes. The Node.js SDK maps them to APIError — see Node.js — Uptime monitoring for SDK-level examples.

StatusMeaning
400Invalid parameters — interval_secs or timeout_secs out of bounds.
401Missing or invalid API key.
402Monitor quota reached for your plan, or interval_secs is below your plan's minimum.
404Monitor not found, or it belongs to a different account.

Plan limits

PlanMonitorsMin intervalCheck history
Free55 min (300s)7 days
Starter201 min (60s)30 days
Pro10030s90 days
BusinessUnlimited30s90 days

The monitor count is a hard ceiling — creating a monitor when you're at the limit returns a 402. Upgrade at app.tickstem.dev/dashboard/billing.

Heartbeat Monitoring

A heartbeat is a dead-man's switch: your job POSTs to a ping URL after each successful run. If the ping stops arriving within the expected interval + grace window, Tickstem sends you an email alert.

  • Each heartbeat gets a unique 64-character hex token. The token itself is the credential — no API key needed to ping.
  • After two consecutive missed intervals the heartbeat flips to failing status and an alert is sent.
  • The grace window is an extra buffer after the deadline before any alert fires — useful for jobs with variable runtime.
  • Pause a heartbeat during planned maintenance so missed pings don't trigger alerts.
Token = credential. Anyone with the ping token can record a ping. Keep it out of public repos. You can delete and recreate a heartbeat to rotate the token.

Create a heartbeat

From the dashboard: Heartbeats → New heartbeat. Or via the REST API:

curl -X POST https://api.tickstem.dev/v1/heartbeats \
  -H "Authorization: Bearer $TICKSTEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "daily backup",
    "interval_secs": 86400,
    "grace_secs": 3600
  }'

The response includes a token field — this is the unique credential used to ping this heartbeat. Copy it and store it alongside your job:

{
  "id": "hb_01abc...",
  "name": "daily backup",
  "token": "a3f8c2d1e4b5f6...",
  "interval_secs": 86400,
  "grace_secs": 3600,
  "status": "active"
}

Parameters

FieldTypeDescription
name*stringHuman-readable label shown in the dashboard.
interval_secsintExpected time between pings in seconds. Range: 60–86400. Default: 3600 (1 hour).
grace_secsintBuffer after the deadline before alerting. Range: 0–86400. Default: 300 (5 minutes).

Heartbeat status values

StatusMeaning
activeReceiving pings within the expected window.
failingTwo or more consecutive intervals missed. Email alert has been sent.
pausedAlert suppression active. Missed pings are ignored.

Pause, resume, delete

# pause
curl -X PATCH https://api.tickstem.dev/v1/heartbeats/{id} \
  -H "Authorization: Bearer $TICKSTEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"paused"}'

# resume
curl -X PATCH https://api.tickstem.dev/v1/heartbeats/{id} \
  -H "Authorization: Bearer $TICKSTEM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"active"}'

# delete (removes the heartbeat and all ping history)
curl -X DELETE https://api.tickstem.dev/v1/heartbeats/{id} \
  -H "Authorization: Bearer $TICKSTEM_API_KEY"

Pinging

Once you have created a heartbeat and copied its token, call the ping endpoint at the end of every successful job run. No API key is needed — the token in the URL is the credential.

Step 1: Get your token — from the dashboard (Heartbeats → your heartbeat → token field) or from the token field in the create response above.

Step 2: Call the ping URL after every successful run:

# Replace the token below with your actual token from the dashboard
curl -X POST https://api.tickstem.dev/v1/heartbeats/a3f8c2d1e4b5f6.../ping

The response is {"status":"ok"} on success, or {"status":"paused"} if the heartbeat is currently paused.

Ping failures should be non-fatal — log the error but don't let a transient network issue block your job from completing.

import "github.com/tickstem/heartbeat"

client := heartbeat.New(os.Getenv("TICKSTEM_API_KEY"))

// At the end of every successful run:
if err := client.Ping(ctx, token); err != nil {
    log.Println("heartbeat ping failed:", err) // non-fatal
}

Plan limits

PlanHeartbeats
Free5
Starter20
Pro100
BusinessUnlimited

The heartbeat count is a hard ceiling — creating one when you're at the limit returns a 402. Upgrade at app.tickstem.dev/dashboard/billing.

Node.js SDK — Installation

The Node.js SDK is split into focused packages. Install only what you need — the same API key works for all of them.

Cron jobs

npm install @tickstem/cron
# or
pnpm add @tickstem/cron

Email verification

npm install @tickstem/verify
# or
pnpm add @tickstem/verify

Uptime monitoring

npm install @tickstem/uptime
# or
pnpm add @tickstem/uptime

Heartbeat monitoring

npm install @tickstem/heartbeat
# or
pnpm add @tickstem/heartbeat

Requires Node.js 18 or later (uses native fetch). Works with JavaScript and TypeScript. ESM only — add "type": "module" to your package.json, or use a bundler (Next.js, Vite, esbuild).

Node.js — Cron jobs

Create a client

import { CronClient } from "@tickstem/cron"

const cron = new CronClient(process.env.TICKSTEM_API_KEY)

Register a job

const job = await cron.register({
  name: "daily-cleanup",
  schedule: "0 2 * * *",
  endpoint: "https://yourapp.com/jobs/cleanup",
})

console.log(job.id, job.next_run_at)

List, pause, resume, delete

const jobs = await cron.list()
await cron.pause(jobId)
await cron.resume(jobId)
await cron.delete(jobId)

Execution history

const executions = await cron.executions(jobId)
for (const e of executions) {
  console.log(e.id, e.status, e.duration_ms)
}

Node.js — Email verification

Create a client

import { VerifyClient } from "@tickstem/verify"

const verify = new VerifyClient(process.env.TICKSTEM_API_KEY)

Verify an email

const result = await verify.verify("[email protected]")

if (!result.valid) {
  console.log(result.reason)
}
if (result.disposable) {
  return { error: "Disposable email addresses are not allowed." }
}
if (result.role_based) {
  return { error: "Please use a personal email address." }
}

History

const history = await verify.history()
for (const entry of history) {
  console.log(entry.email, entry.valid, entry.created_at)
}

Node.js — Uptime monitoring

Create a client

import { UptimeClient } from "@tickstem/uptime"

const uptime = new UptimeClient(process.env.TICKSTEM_API_KEY)

Create and manage monitors

// create
const monitor = await uptime.create({
  name: "Production API",
  url: "https://api.yourapp.com/health",
  interval_secs: 60,
  timeout_secs: 10,
})

// list all monitors
const monitors = await uptime.list()

// get a single monitor
const m = await uptime.get(monitor.id)

// pause / resume / delete
await uptime.pause(monitor.id)
await uptime.resume(monitor.id)
await uptime.delete(monitor.id)

Check history

const checks = await uptime.checks(monitor.id)

for (const check of checks) {
  console.log(check.status, check.duration_ms + "ms", check.checked_at)
}
// check.status: "up" | "down" | "timeout"
// check.status_code: number | null
// check.error: string

Error handling

import { isUnauthorized, isQuotaExceeded, APIError } from "@tickstem/uptime"

try {
  await uptime.create({ name: "x", url: "https://x.com" })
} catch (err) {
  if (isQuotaExceeded(err)) {
    // monitor limit reached, or interval_secs below plan minimum
  }
  if (err instanceof APIError) {
    console.log(err.statusCode, err.message)
  }
}

Node.js — Heartbeat monitoring

Create a client

import { HeartbeatClient } from "@tickstem/heartbeat"

const hb = new HeartbeatClient(process.env.TICKSTEM_API_KEY)

Create and manage heartbeats

// create once — save the token somewhere permanent
const heartbeat = await hb.create({
  name: "daily backup",
  interval_secs: 86400,
  grace_secs: 3600,
})

// list all heartbeats
const all = await hb.list()

// get a single heartbeat
const h = await hb.get(heartbeat.id)

// update fields
await hb.update(heartbeat.id, { name: "weekly backup", interval_secs: 604800 })

// pause / resume / delete
await hb.pause(heartbeat.id)
await hb.resume(heartbeat.id)
await hb.delete(heartbeat.id)

Pinging

// at the end of every successful job run — no API key needed
try {
  await hb.ping(heartbeat.token)
} catch (err) {
  console.error("heartbeat ping failed:", err) // non-fatal — don't block your job
}

Ping history

const pings = await hb.pings(heartbeat.id, { limit: 50 })

for (const ping of pings) {
  console.log(ping.pinged_at)
}

Node.js — Error handling

import { isUnauthorized, isQuotaExceeded, APIError } from "@tickstem/cron"

try {
  await cron.list()
} catch (err) {
  if (isUnauthorized(err)) {
    // invalid or revoked API key
  }
  if (isQuotaExceeded(err)) {
    // monthly execution limit reached
    // upgrade at app.tickstem.dev/dashboard/billing
  }
  if (err instanceof APIError) {
    console.log(err.statusCode, err.message)
  }
}
Same helpers for verify: Import isUnauthorized and isQuotaExceeded from @tickstem/verify for identical error handling in the email verification client.

Python SDK — Installation

A single package covers all four tools. Install only what you need — the same API key works for everything.

pip install tickstem

Requires Python 3.11 or later. The only runtime dependency is httpx.

Same API key as all other SDKs — no additional credentials needed.

Python SDK — Cron jobs

from tickstem import CronClient, CronRegisterParams

client = CronClient(os.environ["TICKSTEM_API_KEY"])

job = client.register(CronRegisterParams(
    name="daily-cleanup",
    schedule="0 2 * * *",
    endpoint="https://yourapp.com/jobs/cleanup",
    method="POST",
    timeout_secs=60,
))

print(job.id, job.next_run_at)

List, pause, resume, delete

jobs = client.list()
job  = client.get(job_id)

client.pause(job_id)
client.resume(job_id)
client.delete(job_id)

Execution history

executions = client.executions(job_id)
for e in executions:
    print(e.status, e.duration_ms, "ms")

Python SDK — Email verification

from tickstem import VerifyClient

client = VerifyClient(os.environ["TICKSTEM_API_KEY"])

result = client.verify("[email protected]")

if not result.valid:
    raise ValueError(f"Email not accepted: {result.reason}")
if result.disposable:
    raise ValueError("Disposable email addresses are not allowed.")
if result.role_based:
    print("Warning: role-based address")  # warn but don't block

History

history = client.history(limit=50)
for entry in history:
    print(entry.email, entry.valid, entry.created_at)

Python SDK — Uptime monitoring

from tickstem import UptimeClient, UptimeCreateParams, Assertion

client = UptimeClient(os.environ["TICKSTEM_API_KEY"])

monitor = client.create(UptimeCreateParams(
    name="Production API",
    url="https://api.yourapp.com/health",
    interval_secs=60,
    assertions=[
        Assertion(source="status_code",   comparison="eq", target="200"),
        Assertion(source="response_time", comparison="lt", target="2000"),
    ],
))

monitors = client.list()
checks   = client.checks(monitor.id, limit=50)

for check in checks:
    print(check.status, check.duration_ms, "ms")

client.delete(monitor.id)

Python SDK — Heartbeat monitoring

from tickstem import HeartbeatClient, HeartbeatCreateParams

client = HeartbeatClient(os.environ["TICKSTEM_API_KEY"])

hb = client.create(HeartbeatCreateParams(
    name="daily backup",
    interval_secs=86400,
    grace_secs=3600,
))

# At the end of every successful job run — no API key needed
try:
    client.ping(hb.token)
except Exception as e:
    print("heartbeat ping failed:", e)  # non-fatal

Pause, resume, delete

client.pause(hb.id)
client.resume(hb.id)
client.delete(hb.id)

pings = client.pings(hb.id, limit=50)

Python SDK — Error handling

from tickstem import CronClient, APIError, is_unauthorized, is_quota_exceeded

client = CronClient(os.environ["TICKSTEM_API_KEY"])

try:
    jobs = client.list()
except APIError as err:
    if is_unauthorized(err):
        print("invalid or revoked API key")
    elif is_quota_exceeded(err):
        print("monthly limit reached — upgrade at app.tickstem.dev/dashboard/billing")
    else:
        print(f"error {err.status}: {err.message}")
Context manager: Use with CronClient(...) as client: to ensure the underlying HTTP connection is closed when you're done.

Status Pages

Every Tickstem account can publish a public status page that auto-generates from its uptime monitors and heartbeat monitors. The page updates itself — no manual incident posts, no maintenance required.

Your status page shows two sections:

  • Services — uptime monitors with current status (Operational / Degraded) and 30-day uptime percentage.
  • Scheduled Jobs — heartbeat monitors with current status (Running / Missing) and last ping time.
No code required. Status pages are configured entirely through the dashboard. There is no API endpoint for creating or updating status pages.

Setup

Go to Dashboard → Status Page and:

  1. Toggle Enable on.
  2. Choose a slug — lowercase letters, numbers, and hyphens. Your page will be at tickstem.dev/status/{slug}.
  3. Optionally set a title (shown as the page heading).
  4. Set visibility to Private while you set up, then flip to Public when ready to share.
VisibilityWho can see it
PrivateOnly you (while logged in) — useful for previewing before going live.
PublicAnyone with the URL — share with your users.

What appears on the page

The page is built from your existing monitors and heartbeats — no extra configuration needed.

SourceShown asMetric
Uptime monitor — activeOperational30-day uptime %
Uptime monitor — failingDegraded30-day uptime %
Uptime monitor — pausedHidden
Heartbeat — active, pingedRunningLast seen timestamp
Heartbeat — failingMissingLast seen timestamp
Heartbeat — pausedHidden

The overall status banner at the top shows All systems operational when every visible monitor and heartbeat is healthy, and Some systems are experiencing issues when any is failing.

All public status pages include a Powered by Tickstem footer link.

Documentation