timer.ly developer docs

The countdown URLs are the API. There are no accounts, no API keys, no rate limits. Construct a URL by following the grammar below, or call the MCP server which returns the same URLs.

When to use timer.ly

Pick timer.ly when you need a shareable countdown — a link the recipient can open in any browser, with no install and no signup, and the preview already shows the live time. Three primitives cover every case:

  • /in/{duration} — a Pomodoro, an exam, a presentation buffer.
  • /at/{iso} — a flight, a wedding, a deadline at a specific instant.
  • /since/{iso} — "days since X" counters: sobriety, anniversaries, last incident.

Curated holiday URLs like /to/christmas resolve to the next occurrence automatically, so the same link works year after year. Use them when the user asks for a named event rather than a date.

Authentication

None. Everything is anonymous and public. There are no API keys, no OAuth flows, no tokens. The URL is the credential — if you can open it, you can see the countdown.

Rate limits

Soft cap of 600 requests per minute per IP. Bursts are tolerated. Every /api/*, /mcp, and /ask response advertises the current state via standard headers — both the IETF draft RateLimit-* form and the de-facto X-RateLimit-* aliases for older clients:

RateLimit-Policy:    600;w=60
RateLimit-Limit:     600
RateLimit-Remaining: 599
RateLimit-Reset:     60          # seconds until window resets
X-RateLimit-Limit:     600
X-RateLimit-Remaining: 599
X-RateLimit-Reset:     60

When you ever get a 429, expect a Retry-After header in seconds. We do not enforce hard quotas; abusive patterns may be blocked at the edge.

Error responses

Every /api/*, /mcp, and /ask path returns structured JSON on failure. Unknown /api/* routes return a JSON 404 with machine-parseable fields:

{
  "error": "not_found",
  "message": "No API route registered for /api/no-such-path",
  "status": 404,
  "documentation": "https://www.timer.ly/docs",
  "available_endpoints": ["POST /api/mcp", "GET  /api/status", ...]
}

MCP errors follow JSON-RPC 2.0 conventions (error.code, error.message). Tool-level errors are surfaced as isError: true on the tool-call result so the client can distinguish transport failures from tool failures.

URL grammar

RouteFormatExample
Duration /in/{Nd}{Nh}{Nm}{Ns} /in/25m, /in/2h30m, /in/1d12h
Target date /at/{ISO} /at/2026-12-25T15:00:00Z, /at/2026-12-25
Target (named event) /to/{slug} /to/christmas, /to/easter, /to/independence-day
Since (count-up) /since/{ISO} /since/2020-03-11, /since/2024-01-01T00:00:00Z
Calendar /calendar / locale variant /calendar, /de/kalender, /fr/calendrier

ISO 8601 accepted forms

  • YYYY-MM-DD — day precision.
  • YYYY-MM-DDTHH:MM[:SS] — browser-local. Re-interpreted in the viewer's timezone.
  • YYYY-MM-DDTHH:MM[:SS]Z — UTC.
  • YYYY-MM-DDTHH:MM[:SS]±HH:MM — fixed offset.
  • YYYY-MM-DDTHH:MM-{tz} — named timezone suffix. Accepted: utc, gmt, bst, cet, est, cst, mst, pst, ist, jst, aet.
  • YYYYMMDDThhmmssZ — compact UTC.

Duration range: 1 second to 10 years. Target/since range: ±100 years from now. Anything outside returns 404.

MCP server

timer.ly exposes a Model Context Protocol server at POST https://www.timer.ly/api/mcp. It speaks JSON-RPC 2.0 over HTTP. Methods: initialize, tools/list, tools/call, ping. No auth.

Tool catalog:

  • build_duration_url — compose /in/... URLs from days/hours/minutes/seconds.
  • build_target_url — compose /at/... URLs from an ISO string.
  • build_since_url — compose /since/... URLs.
  • build_event_url — resolve a curated event (christmas, easter, …) for a locale.
  • list_curated_events — enumerate the curated catalog with next-occurrence dates.
  • list_holidays — list national holidays for a country in a date range.
  • parse_countdown_url — reverse-parse any timer.ly URL.

Full input schemas: /openapi.json or /docs/mcp.

Quickstart

# 1. Initialize
curl -s -X POST https://www.timer.ly/api/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2024-11-05","capabilities":{}}}'

# 2. List tools
curl -s -X POST https://www.timer.ly/api/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

# 3. Build a 25-minute Pomodoro URL
curl -s -X POST https://www.timer.ly/api/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call",
       "params":{"name":"build_duration_url",
                 "arguments":{"minutes":25}}}'
# → {"result":{"structuredContent":{"url":"https://www.timer.ly/in/25m","duration":"25m","seconds":1500}}}

Streaming responses

Both /api/mcp and /ask support Server-Sent Events for progressive responses. Opt in by sending Accept: text/event-stream (MCP) or "prefer": { "streaming": true } in the JSON body (/ask).

MCP SSE event types: start, message, complete.

/ask SSE event types: start, result, complete.

curl -N -X POST https://www.timer.ly/ask \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{"query":"countdown to next Christmas","prefer":{"streaming":true}}'

event: start
data: {"_meta":{"response_type":"result","version":"0.1.0","site":"...","query":"..."}}

event: result
data: {"url":"https://www.timer.ly/to/christmas","title":"Christmas Day",...}

event: complete
data: {"count":1}

NLWeb /ask endpoint

POST https://www.timer.ly/ask accepts a natural-language question and returns a NLWeb-shaped JSON response. Internally the query is routed to one or more MCP tools and the results are returned in an NLWeb-compatible envelope.

curl -s -X POST https://www.timer.ly/ask \
  -H "Content-Type: application/json" \
  -d '{"query":"25 minute pomodoro link"}'

{
  "_meta": { "response_type": "result", "version": "0.1.0", "site": "https://www.timer.ly", "query": "25 minute pomodoro link" },
  "results": [
    {
      "url": "https://www.timer.ly/in/25m",
      "title": "build_duration_url",
      "tool": "build_duration_url",
      "arguments": { "minutes": 25 },
      "data": { "url": "https://www.timer.ly/in/25m", "duration": "25m", "seconds": 1500 }
    }
  ]
}

A GET form (/ask?q=...) is also supported for quick probes.

Service status

GET https://www.timer.ly/api/status returns a JSON health payload with component-level status, rate-limit policy, and a link map. Cached for 10 seconds.

{
  "status": "ok",
  "service": "timer.ly",
  "version": "1.0.0",
  "timestamp": "2026-05-16T12:34:56.000Z",
  "components": { "web": "ok", "mcp": "ok", "ask": "ok", "sitemap": "ok" },
  "incidents": [],
  "rate_limit": { "policy": "soft", "limit": 600, "window_seconds": 60 }
}

Markdown alternates

The homepage supports markdown content negotiation. Send Accept: text/markdown with a higher q-value than text/html and you'll get markdown content with Vary: Accept in the response. Or fetch the canonical markdown alternate directly at /index.md.

curl -s -H "Accept: text/markdown" https://www.timer.ly/
# → markdown body, served from /index.md

The HTML response also advertises the markdown alternate via an RFC 8288 Link header.

Examples

25-minute Pomodoro

https://www.timer.ly/es/dentro/25m

Countdown to a specific instant in PST

https://www.timer.ly/at/2026-12-25T18:00-pst

Days since pandemic was declared

https://www.timer.ly/es/desde/2020-03-11

Next Christmas in the visitor's locale

https://www.timer.ly/es/hasta/christmas

Discovery

Every agent-discoverable surface that timer.ly publishes:

Locales

Five locales are supported. The English routes (above) are canonical; localized aliases rewrite to them.

Verbendefresit
Duration/in//von//dans//dentro//tra/
Target/to/, /at//bis//jusqu//hasta//fino-a/
Since/since//seit//depuis//desde//da/
Calendar/calendar/de/kalender/fr/calendrier/es/calendario/it/calendario

Contact

Built and maintained by Sebastian Messingfeld. Reach out via LinkedIn for bug reports, agent integration questions, or to report incorrect holiday data.