zmanim.cc APIv1
Contents

Developer documentation

Shabbat and Yom Tov times, as plain files

Candle lighting, exit and every rest period (Shabbat, Yom Tov, Yom Kippur and the runs of them) for over 1,400 cities, 20 years ahead. Static JSON, text, C header and calendar files. No key, no sign-up, no SDK.

Base URL
https://api.zmanim.cc/v1/
Auth
None
Horizon
20 years, refreshed yearly

Before you build: exit times may still move by up to 1 minute after a pending halachic ruling on rounding. Every payload says "stable": false. See stable=false.

1. Quick start

Pick a city slug from /v1/cities.json (for example jerusalem, new-york, london) and download its rest windows:

Shell
curl https://api.zmanim.cc/v1/jerusalem/windows.json
JavaScript
const res = await fetch("https://api.zmanim.cc/v1/jerusalem/windows.json");
const { windows } = await res.json();
const now = Date.now() / 1000;
const next = windows.find((w) => (w.end_ts ?? w.start_ts + 4 * 86400) > now);
console.log(next.title.en, next.start, next.end);

The response, trimmed to one window:

JSON
{
  "api_version": 1,
  "data_version": "dbef44f6582f",
  "rounding": {
    "entry": "floor_minute",
    "exit": "engine_minute",
    "stable": false,
    "note": "Candle lighting and every other zman are truncated to the minute, exactly as zmanim.cc displays them, except the exit times, which are the engine minute: havdalah (8.5 degrees) and tzeit hakochavim are rounded up to the next minute, and Rabbeinu Tam is sunset rounded to the nearest minute plus 72 minutes. A pending rabbinic ruling may move exit minutes by 1; such a change is not a breaking change."
  },
  "docs_url": "https://api.zmanim.cc/",
  "disclaimer_url": "https://api.zmanim.cc/#terms",
  "city": {
    "slug": "jerusalem",
    "name": {
      "he": "ירושלים",
      "en": "Jerusalem"
    },
    "country": "Israel",
    "lat": 31.7683,
    "lng": 35.2137,
    "elevation_m": 754,
    "tz": "Asia/Jerusalem",
    "il": true,
    "candle_minutes": 40,
    "candle_basis": "elevation"
  },
  "coverage": {
    "from_year": 2026,
    "to_year": 2046
  },
  "windows": [
    {
      "id": "2026-10-02-shmini-atzeret",
      "kind": "shabbat_yomtov",
      "title": {"he":"שמיני עצרת (שמחת תורה) + שבת","en":"Shmini Atzeret (Simchat Torah) + Shabbat"},
      "start": "2026-10-02T17:47:00+03:00",
      "start_ts": 1790952420,
      "end": "2026-10-03T18:58:00+03:00",
      "end_ts": 1791043080,
      "start_reason": null,
      "end_reason": null
    },
    ...
  ]
}

Three rules cover almost everything:

  1. Think in windows, not days. A window runs from candle lighting to the final exit, even across two or three days. Rest while start_ts <= now < end_ts.
  2. Never do time zone math. Use the *_ts epoch seconds, or the ISO strings, which already carry the city's offset.
  3. Cache, then check version.json. Files change only when data_version changes.

2. Files

URLWhatSize (compressed)
/v1/cities.jsonEvery city and its minhag (slug, names, lat/lng, tz, il, candle_minutes, candle_basis)290 KB (42 KB)
/v1/{city}/windows.jsonEvery window for 20 years, bounds only: id, kind, title, start, end (+ _ts, _reason)300 KB (35 KB)
/v1/{city}/windows-{year}.jsonThe windows ending in one year, in full detail (adds erev, days[], lighting[])33 KB (5 KB)
/v1/{city}/windows.txtstart_ts end_ts kind per line, 20 years36 KB
/v1/{city}/windows.hC header, 20 years42 KB source, about 10 KB compiled
/v1/{city}/windows.icsCalendar feed, about 2 years46 KB
/v1/{city}/{YYYY-MM-DD}.jsonEvery zman for one day (computed)4 KB
/v1/version.jsonThe cheap "did anything change?" check100 B
/v1/index.jsonDiscovery: coverage, year_files, URL templates1 KB
  • HTTPS only, GET/HEAD, CORS open (Access-Control-Allow-Origin: *). UTF-8, compressed on request.
  • Every JSON payload starts with api_version, data_version, rounding, docs_url, disclaimer_url; per-city files then carry the city object from cities.json.
  • A window is in the year file of its last rest day, so the year files together equal windows.json. Only the years in index.json year_files exist (today 2026 to 2028); long-lived clients should use windows.json.
  • Every key is always present; a value that does not exist is null. New keys may appear; ignore the ones you do not know.
  • A slug is never removed. A duplicate is retired with a 301 to the kept slug (for example kiryat-sefer → modiin-illit); follow redirects.
  • Epochs pass 231 in 2038: use uint32_t or 64-bit, never a signed 32-bit time_t.

3. Day endpoint

/v1/{city}/{YYYY-MM-DD}.json gives every zman for one civil day, 1900 to 2100: alot_hashachar, misheyakir, sunrise, sof_zman_shma_mga/_gra, sof_zman_tfilla_mga/_gra, chatzot, mincha_gedola_gra/_mga, mincha_ketana_gra/_mga, plag_hamincha, candle_lighting, sunset, tzeit_hakochavim, havdalah, rabbeinu_tam, chatzot_layla, each with a _ts. It also has the Hebrew date, holidays, the parasha, and rest/next_rest (full window objects). Sunrise and sunset are at sea level; candle_lighting is set only on an evening that lights before sunset; havdalah only on the last day of a window.

Example, /v1/jerusalem/2026-10-16.json (trimmed):

JSON (trimmed)
{
  "api_version": 1,
  ...
  "date": "2026-10-16",
  "day_of_week": 5,
  "hebrew_date": {"he":"ה׳ חשוון תשפ״ז","day":5,"month":"Cheshvan","month_num":8,"year":5787},
  "is_shabbat": false,
  "is_yomtov": false,
  "parasha": {"id":"noach","he":"נח","en":"Noach"},
  "zmanim": {
    "sunrise": "2026-10-16T06:42:00+03:00",
    "sunrise_ts": 1792122120,
    "candle_lighting": "2026-10-16T17:30:00+03:00",
    "candle_lighting_ts": 1792161000,
    "sunset": "2026-10-16T18:06:00+03:00",
    "sunset_ts": 1792163160,
    "tzeit_hakochavim": "2026-10-16T18:27:00+03:00",
    "tzeit_hakochavim_ts": 1792164420,
    "havdalah": null,
    "havdalah_ts": null,
    ...
  },
  "rest": {"id": "2026-10-16-shabbat", "start": "2026-10-16T17:30:00+03:00", "end": "2026-10-17T18:42:00+03:00", ...},
  "next_rest": {...}
}

Unlike the files it is computed on request, then cached at the edge for a day. It runs on a free plan with 100,000 requests a day shared by all users. Use it for daily zmanim, one request per city per day, never to loop over dates: everything about Shabbat and Yom Tov is in the static files.

4. Key concepts

Rest windows

A window is one continuous period with no melacha, from candle lighting on the erev to the exit (havdalah, 8.5°) of the last rest day. kind is shabbat, yomtov, shabbat_yomtov (touching, in any order, 2 or 3 days), yomkippur or shabbat_yomkippur; treat any unknown kind as resting. id (the erev date and a label) is unique per city and stable, good as a database key.

A device that switches off at Saturday's havdalah would break Rosh Hashana 2026, which starts on Friday night: Karnei Shomron rests from Friday to Sunday night, one window. The year file shows the detail: the rest days[] and each evening's lighting[] (before_sunset, or from_existing_flame after a time):

JSON, windows-2026.json
{
  "id": "2026-09-11-rosh-hashana",
  "kind": "shabbat_yomtov",
  "title": {"he":"ראש השנה + שבת","en":"Rosh Hashana + Shabbat"},
  "erev": "2026-09-11",
  "start": "2026-09-11T18:29:00+03:00",
  "start_ts": 1789140540,
  "start_reason": null,
  "end": "2026-09-13T19:26:00+03:00",
  "end_ts": 1789316760,
  "end_reason": null,
  "days": [
    {"date":"2026-09-12","kind":"shabbat_yomtov","holiday":{"id":"rosh-hashana-1","he":"ראש השנה א׳","en":"Rosh Hashana I"},"parasha":null},
    {"date":"2026-09-13","kind":"yomtov","holiday":{"id":"rosh-hashana-2","he":"ראש השנה ב׳","en":"Rosh Hashana II"},"parasha":null}
  ],
  "lighting": [
    {"date":"2026-09-11","type":"before_sunset","at":"2026-09-11T18:29:00+03:00","at_ts":1789140540,"after":null,"after_ts":null,"reason":null,"existing_flame":false},
    {"date":"2026-09-12","type":"from_existing_flame","at":null,"at_ts":null,"after":"2026-09-12T19:27:00+03:00","after_ts":1789230420,"reason":null,"existing_flame":true}
  ]
}

Not rest windows: Chol HaMoed, Purim, Chanukah, Tisha B'Av and the minor fasts.

City minhag

Candle lighting follows each city's custom, which is why the API works by city and not by coordinates. candle_minutes is minutes before sunset (Jerusalem 40, Haifa 30, Tel Aviv 22, New York 18). candle_basis says which sunset: sea_level, elevation (the city's own, e.g. Jerusalem) or jerusalem (Petah Tikva lights when Jerusalem does). il picks the Israeli or diaspora calendar: Yom Tov length, parasha, and the daily tzeit_hakochavim (sunset + 20 minutes in Israel, 6° abroad). The exit is 8.5° everywhere.

Times: ISO and epoch

Every instant comes twice: "start": "2026-09-11T18:29:00+03:00" is the city's wall clock with its offset, DST included (characters 11 to 16 are the HH:MM to show), and "start_ts": 1789140540 is the same instant in UTC epoch seconds, for comparing with now. Plain dates (erev, days[].date) are civil dates in the city.

Rounding and stable: false

Times are exactly what zmanim.cc shows: candle lighting and daytime zmanim truncated to the minute (rounding.entry: "floor_minute"); havdalah and tzeit rounded up, Rabbeinu Tam sunset to the nearest minute plus 72 (rounding.exit: "engine_minute"). A pending halachic ruling may move exit minutes by 1. That is not a breaking change: only values and data_version change. So check version.json daily, keep at least 1 minute of margin on each side in devices, and never hardcode the times anywhere you cannot refresh.

Nulls

Far north in summer the sun may never reach 8.5° below the horizon, so there is no exit. The API does not invent one: end and end_ts are null and end_reason says why (depression_not_reached or no_sunset). Today only Saint Petersburg is affected, around June. Never read null as "no Shabbat" (in JavaScript now < null is false): replace it with a conservative bound first, as the quick start does. The device files cannot hold null, so they carry a flagged placeholder that errs toward resting longer (12:00 local on the day after the last rest day):

windows.txt
# 2026-05-29-shabbat: no computed end (depression_not_reached): 12:00 local on the day after - conservative placeholder, not a zman
1780080060 1780218000 shabbat

If you serve such a city, ask your rav what the exit should be. In the day endpoint any zman the sun does not reach is null too.

5. Offline devices

Numbers only, UTC epoch seconds, no time zone or DST on the device. Rest while start <= now < end.

windows.txt, for a device that goes online

One start_ts end_ts kind per line; # lines are comments (the header carries data_version and # count:). Store it in flash and run from that copy, so the device never needs the network during Shabbat. Accept a download only when it is complete: HTTP 200, the row count equals # count:, every row has start < end. Use HTTPS with a CA bundle (never setInsecure()) and set the clock from NTP.

Text (trimmed)
# zmanim.cc rest windows (Shabbat, Yom Tov, Yom Kippur): no-melacha periods
# api_version: 1
# city: karnei-shomron (Karnei Shomron)
# tz: Asia/Jerusalem (numbers below are UTC epoch seconds, no timezone math needed)
# data_version: dbef44f6582f
# coverage: 2026-2046
# count: 1184
# placeholders: 0
# rounding: start = candle lighting truncated to the minute; end = havdalah (8.5 deg), engine minute (rounded up); stable=false, an exit may move by 1 minute after a pending ruling
# docs: https://api.zmanim.cc/#txt
# format: start_ts end_ts kind   (resting while start_ts <= now < end_ts; kind = shabbat|yomtov|shabbat_yomtov|yomkippur|shabbat_yomkippur)
1767363840 1767454020 shabbat
1767969000 1768059180 shabbat
...
1789140540 1789316760 shabbat_yomtov
1789744740 1789834620 shabbat

windows.h, for a device that is never online

A C/C++ header to compile into the firmware: zmanim_windows[ZMANIM_WINDOW_COUNT][2] (uint32_t start, end), zmanim_window_kinds[], and ZMANIM_READ_U32(), which reads from PROGMEM on AVR. About 10 KB of flash for 20 years; it fits an Arduino Uno. Include it in one source file only.

windows.h (excerpt)
#define ZMANIM_DATA_VERSION "dbef44f6582f"
#define ZMANIM_CITY "karnei-shomron"
#define ZMANIM_FROM_YEAR 2026
#define ZMANIM_TO_YEAR 2046
#define ZMANIM_WINDOW_COUNT 1184

static const uint32_t zmanim_windows[ZMANIM_WINDOW_COUNT][2] ZMANIM_PROGMEM = {
  {1767363840UL, 1767454020UL},
  {1767969000UL, 1768059180UL},
  ...
};
C
/* 1 = resting, 0 = weekday, -1 = unknown (clock unset or table ran out: stay resting) */
int zmanim_state(uint32_t now, uint32_t margin) {
  for (uint16_t i = 0; i < ZMANIM_WINDOW_COUNT; i++) {
    uint32_t start = ZMANIM_READ_U32(&zmanim_windows[i][0]);
    uint32_t end = ZMANIM_READ_U32(&zmanim_windows[i][1]);
    if (now + margin < start) return 0;
    if (now < end + margin) return 1;
  }
  return -1;
}

Clock drift and refresh

  • Keep the real-time clock in UTC and use a temperature compensated one such as the DS3231 (about 1 minute a year). A DS1307 can drift 10 minutes a year.
  • Margin on both sides (start early, end late): 1 minute for the rounding ruling, 1 to spare, plus 1 per year between clock checks. NTP: 2 minutes; checked yearly: 3; every 5 years: 7; never: about 22.
  • Fail safe: when the clock lost power, is unset, or the table has run out (after ZMANIM_TO_YEAR), hold the resting state and show it.
  • Refresh: once a day, outside a window, fetch version.json (about 100 bytes) and download the file again only when data_version changed. A device that goes online even once a year (a service visit, a phone app, USB) gets any correction and moves the 20-year horizon forward.
JSON, /v1/version.json
{"api_version":1,"data_version":"dbef44f6582f","coverage":{"from_year":2026,"to_year":2046}}

6. Calendar feed

https://api.zmanim.cc/v1/{city}/windows.ics (or webcal://) is an iCalendar feed with one event per window, from candle lighting to the exit, in UTC, covering from a month back to about two years ahead. Subscribe to it in Google Calendar (Other calendars › From URL), Apple Calendar or Outlook, or in Home Assistant's Remote Calendar integration, whose calendar entity is on during every window. SUMMARY is the Hebrew and English title, UID is {id}@{city}.api.zmanim.cc, CATEGORIES is the kind. Calendar apps refresh on their own schedule (Google can take a day or more), so do not drive a device from a subscribed calendar.

7. Errors, caching, versions

  • Errors are JSON: {"error": {"code", "message", "docs"}}. Codes: invalid_date and date_out_of_range (400), city_not_found (404, slugs are lowercase), not_found (404; for a missing year file the message lists the years), method_not_allowed (405), internal_error (500). If the day endpoint's daily limit is used up, Cloudflare answers with its own page: treat any non-200 as "no data" and keep what you have.
  • Caching: data files a day, index.json and version.json an hour, windows.ics 6 hours, day endpoint a day. A query string does not bypass the cache; use version.json. Do not poll anything more than hourly, and send a User-Agent that names your app.
  • Versions: /v1/ changes are additive only; a breaking change would ship as /v2/ with at least 90 days of overlap. data_version is a hash of the data, so it changes only when a value does (each January, and after a correction). Compare it for equality only.

8. Terms

  • Free for any use, including commercial. No key, no sign-up.
  • Attribution requested: where people see the times, please show "Times: zmanim.cc" with a link to https://zmanim.cc.
  • No warranty. The data is provided as is. We work hard to get it right and fix what we find, but we cannot guarantee it is free of errors or that the service is always available.
  • Not a psak halacha. The times follow the methods described on this page and the local customs recorded for each city. For any halachic question, consult your rav.
  • Devices must fail safe. If you control anything with this data, default to the resting state whenever the clock, the network or the data is missing or uncertain, and let people override it by hand.

Contact: bugs, questions or a city to add: admin@zmanim.cc. Please include the URL you called.