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:
curl https://api.zmanim.cc/v1/jerusalem/windows.jsonconst 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:
{
"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:
- 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. - Never do time zone math. Use the
*_tsepoch seconds, or the ISO strings, which already carry the city's offset. - Cache, then check
version.json. Files change only whendata_versionchanges.
2. Files
| URL | What | Size (compressed) |
|---|---|---|
/v1/cities.json | Every city and its minhag (slug, names, lat/lng, tz, il, candle_minutes, candle_basis) | 290 KB (42 KB) |
/v1/{city}/windows.json | Every window for 20 years, bounds only: id, kind, title, start, end (+ _ts, _reason) | 300 KB (35 KB) |
/v1/{city}/windows-{year}.json | The windows ending in one year, in full detail (adds erev, days[], lighting[]) | 33 KB (5 KB) |
/v1/{city}/windows.txt | start_ts end_ts kind per line, 20 years | 36 KB |
/v1/{city}/windows.h | C header, 20 years | 42 KB source, about 10 KB compiled |
/v1/{city}/windows.ics | Calendar feed, about 2 years | 46 KB |
/v1/{city}/{YYYY-MM-DD}.json | Every zman for one day (computed) | 4 KB |
/v1/version.json | The cheap "did anything change?" check | 100 B |
/v1/index.json | Discovery: coverage, year_files, URL templates | 1 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 thecityobject fromcities.json. - A window is in the year file of its last rest day, so the year files together equal
windows.json. Only the years inindex.jsonyear_filesexist (today 2026 to 2028); long-lived clients should usewindows.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
301to the kept slug (for examplekiryat-sefer→modiin-illit); follow redirects. - Epochs pass 231 in 2038: use
uint32_tor 64-bit, never a signed 32-bittime_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):
{
"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):
{
"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):
# 2026-05-29-shabbat: no computed end (depression_not_reached): 12:00 local on the day after - conservative placeholder, not a zman
1780080060 1780218000 shabbatIf 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.
# 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 shabbatwindows.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.
#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},
...
};/* 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 whendata_versionchanged. 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.
{"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_dateanddate_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.jsonandversion.jsonan hour,windows.ics6 hours, day endpoint a day. A query string does not bypass the cache; useversion.json. Do not poll anything more than hourly, and send aUser-Agentthat names your app. - Versions:
/v1/changes are additive only; a breaking change would ship as/v2/with at least 90 days of overlap.data_versionis 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.