Takbeer Time API
A public, JSON REST API for masjid prayer times, jamat times, jamaat times, and iqamah updates. Build a widget, a Slack bot, a dashboard, or a masjid-finder for your community website.
Introduction
Base URL:
https://takbeertime.com/api
The base URL returns discovery metadata for clients and uptime checks:
curl "https://takbeertime.com/api"
All requests and responses are JSON. The API is read-mostly and most endpoints don't need authentication — read paths are open so a directory app can ship without account plumbing. Write paths (creating masjids, submitting timings, voting, suggesting updates) need a signed-in user.
No ads, ever. The API is free for any genuine use. Sadqa fe sabilillah. Heavy commercial use? Drop us a note so we know what to plan capacity for.
Authentication
Pass the Takbeer Time app JWT returned by /auth/register, /auth/login, or /auth/google in the Authorization header:
Authorization: Bearer <app-jwt>
The backend also accepts a verified Firebase ID token on authenticated routes for compatibility, but the app JWT is the normal client contract. Anonymous calls to read endpoints return public fields only — calls to write or to /users/me/* return 401 without a token.
For local development, the server accepts an X-Dev-User-Email header in lieu of an Authorization bearer token, but only when DEV_AUTH_USER_EMAIL is set in the deploy. Production never accepts it.
POST
/auth/register
Public
5 / hr / IP
Create an email/password account and return { token, user }. If a dev-created user exists without a password, this claims the account; Google-only or deleted accounts return 409.
{
"email": "[email protected]",
"password": "at-least-8-characters",
"fullName": "User Name"
}
POST
/auth/login
Public
10 / 15 min / IP
Sign in with email/password and return { token, user }.
{ "email": "[email protected]", "password": "..." }
POST
/auth/google
Public
20 / 15 min / IP
Exchange a Firebase Google sign-in ID token for the same Takbeer Time app JWT used by email/password auth.
{ "idToken": "firebase-google-id-token" }
Rate limits
Per-user (or per-IP for anonymous), one-hour rolling window. RateLimit-* headers report current state on every response.
| Endpoint | Cap (per hour) |
| POST /submissions | 30 |
| POST /submissions/:id/vote | 60 |
| POST /suggestions | 10 |
| POST /mosques | 5 |
| POST /auth/login | 10 per 15 minutes per IP |
| POST /auth/google | 20 per 15 minutes per IP |
| POST /auth/register | 5 per IP |
| POST /account-deletion-request | 1 pending request per email per hour |
Exceeding the cap returns 429 Too Many Requests. Read endpoints are not rate-limited at present.
Errors
Every error response is JSON with at least error and message fields.
{
"error": "Validation Error",
"message": "lat is required"
}
| Status | Meaning |
| 400 | Validation failed (missing/invalid params) |
| 401 | Not authenticated (missing or expired token) |
| 403 | Authenticated, but not allowed for this resource |
| 404 | Resource not found (or soft-deleted) |
| 429 | Rate limit exceeded |
| 500 | Server error — please report |
Response shape
Paginated endpoints return:
{
"data": [ ... ],
"pagination": {
"page": 1,
"limit": 20,
"totalCount": 281696,
"totalPages": 14085,
"hasMore": true
}
}
Times are stored and returned as 24-hour HH:mm strings. Maghrib is not stored as a clock time — it's astronomical sunset for the masjid's coordinates plus a per-masjid maghribOffset (number of minutes after sunset that takbeer is called). Compute Maghrib client-side; the API only carries the offset.
Masjids
GET
/mosques/recent
Public
Latest active masjids added to the directory. Returns up to 12 records for "recently added" widgets and community dashboards.
GET
/mosques/nearby
Public
Find masjids within a radius of a coordinate, sorted by distance. The active prayer schedule is included on each result so you can label markers without a second round-trip.
Query params
| Name | Type | |
| lat | number | Required. Latitude (-90..90). |
| lng | number | Required. Longitude (-180..180). |
| radius | number | Search radius in meters. Default 5000, max 200000. |
| limit | number | Default 20, max 100. |
Example
curl "https://takbeertime.com/api/mosques/nearby?lat=33.7295&lng=73.0372&radius=5000"
GET
/mosques
Public
Paginated, filterable list. Use this when you don't have coordinates — e.g. to enumerate all masjids in a city.
Query params
| city | string | Filter by city name (case-insensitive). |
| country | string | Filter by country. |
| search | string | Trigram-matched search across name + city. |
| page | number | 1-indexed. Default 1. |
| limit | number | Default 20, max 100. |
GET
/mosques/:id
Public
Full masjid record: location, amenities, the active prayer schedule, the ranked keepers list, and the effective times for the calling user (their preferred keeper, or the top-rated keeper if no preference set).
Response (excerpt)
{
"id": "...",
"name": "Faisal Mosque",
"latitude": 33.7295,
"longitude": 73.0372,
"city": "Islamabad",
"country": "Pakistan",
"amenities": ["parking", "wudhu", "women_section"],
"prayerSchedules": [{
"timings": { "fajr": "05:00", "dhuhr": "13:30", "maghribOffset": 3, ... },
"verificationStatus": "verified"
}],
"keepers": [...],
"effectiveTimings": { "fajr": "05:00", ... },
"effectiveKeeperId": "...",
"effectiveKeeperName": "..."
}
GET
/mosques/:id/keepers
Public
Ranked list of contributors who've submitted timings for this masjid. Each keeper's rating is computed as voteScore × recencyWeight × verifiedBoost × repBoost × keeperBoost. The top-rated keeper's submission is the default for users who haven't picked otherwise.
GET
/mosques/:id/consensus
Public
Per-prayer consensus over recent submissions. Shows the time, confidence (0..1), and contributor count for each prayer. Useful for masjids that don't yet have an active schedule — surface "the community thinks Fajr is around 05:00 (62% confidence)" instead of nothing.
GET
/mosques/:id/reviews
Public
Paginated reviews for a masjid. Sorted by helpful-count then recency.
POST
/mosques
Auth
5 / hr
Add a new masjid to the directory. The caller must be signed in; abuse is rate-limited.
Body
{
"name": "Masjid Al-Falah",
"latitude": 33.7295,
"longitude": 73.0372,
"city": "Islamabad",
"country": "Pakistan",
"addressLine1": "Block 4, Garden Town" // optional
}
PUT
/mosques/:id
Auth
Edit a masjid created by the authenticated user. Returns 403 if another contributor owns the masjid record.
Body
Any field accepted by POST /mosques may be sent as a partial update. If either coordinate changes, send both latitude and longitude.
POST
/mosques/:id/reviews
Auth
Create or update the authenticated user's review for a masjid.
{
"rating": 5,
"timingAccuracyRating": 5,
"cleanlinessRating": 4,
"accessibilityRating": 4,
"title": "Reliable timings",
"reviewText": "The posted jamaat times match the board."
}
POST
/mosques/:id/favorite
Auth
Toggle: if not already favorited, save it; if already, remove. Returns { favorite: true|false }.
Submissions
A "submission" is a contributor's proposed jamat times for a specific masjid. Multiple users can submit; the consensus algorithm + per-keeper rating decide which becomes the active schedule.
GET
/submissions
Public
Paginated submissions, filterable by masjid id and status.
Query params
| mosqueId | uuid | Restrict to a single masjid. |
| status | string | pending | approved | rejected. |
| page, limit | number | Standard pagination. |
GET
/submissions/:id
Public
A single submission with the contributor's profile (name, reputation, verified flag).
POST
/submissions
Auth
30 / hr
Submit timings for a masjid. After creation the consensus pipeline runs — your submission may immediately become the active schedule if confidence is high enough.
Body
{
"mosqueId": "uuid",
"timings": {
"fajr": "05:00",
"dhuhr": "13:30",
"asr": "17:00",
"isha": "20:00",
"jummah": ["13:30"],
"maghribOffset": 3 // minutes after astronomical sunset
},
"notes": "winter schedule" // optional
}
Don't send maghrib as HH:MM. Maghrib is computed client-side from the masjid's coordinates + the offset you submit. The server silently strips any maghrib field.
POST
/submissions/:id/vote
Auth
60 / hr
Up- or down-vote a submission. Voting on your own submission returns 400.
Body
{ "voteType": "upvote" | "downvote" | "report",
"reportReason": "..." // required when voteType is "report" }
POST
/submissions/:id/review
Auth
Verified contributors can approve or reject a pending timing submission. Approval installs a verified active schedule and rewards the submitter.
Body
{
"action": "approve" | "reject",
"reviewNotes": "Checked against the masjid board"
}
Suggestions
A direct user-to-keeper proposal: "I think Fajr should be 05:16 instead of 05:00, can you update your record?" The keeper sees it in their inbox and accepts or declines. Accepting writes the suggested values to the masjid's active schedule (the keeper is authoritative over their own record — no consensus floor needed).
POST
/suggestions
Auth
10 / hr
Send a suggestion to a specific keeper (the user whose timings are currently active for the masjid, or anyone in the keeper list).
Body
{
"toUserId": "uuid", // recipient keeper
"mosqueId": "uuid",
"timings": { "fajr": "05:16", "maghribOffset": 5 },
"notes": "Ramadan schedule starts tomorrow"
}
GET
/suggestions/inbox
Auth
Pending suggestions sent to the authenticated user. Each suggestion includes the sender, the masjid, the proposed times, AND the masjid's current active timings — so a UI can render an old → new diff in one round-trip.
POST
/suggestions/:id/accept
Auth
Mark accepted, install the proposed timings on the masjid's active schedule (merged over current values), and create a TimingSubmission as the keeper for the audit trail. Returns 403 if the calling user isn't the recipient.
POST
/suggestions/:id/decline
Auth
Mark declined with optional note. No schedule change.
Body
{ "note": "These times are wrong, please re-check" } // optional
Users
GET
/users/me
Auth
The authenticated user's profile, default mosque, and counts.
PUT
/users/me
Auth
Update profile fields.
Body (all optional)
{
"fullName": "Junaid Qazi",
"phoneNumber": "+92...",
"preferredLanguage": "ur",
"defaultMosqueId": "uuid" // null clears it
}
GET
/users/me/favorites
Auth
Paginated list of mosques the user has favorited.
PUT
/users/me/preferred-keeper
Auth
Set or clear the user's preferred keeper for one masjid. The masjid detail's effectiveTimings use this keeper's submission when set.
{ "mosqueId": "uuid", "keeperUserId": "uuid" | null }
PUT
/users/me/reminder-prefs
Auth
Mirror the user's reminder preferences server-side so they sync across devices.
{
"enabled": true,
"perPrayer": { "fajr": 10, "dhuhr": 10, "asr": 10, "maghrib": 10, "isha": 10, "jummah": 0 },
"prayerEnabled": { "fajr": true, "dhuhr": true, ... }
}
GET
/users/me/notifications
Auth
Paginated notifications (timing changes, new keepers, etc.). Add ?unread=true to filter.
PATCH
/users/me/notifications/:id/read
Auth
Mark one notification as read. Returns 404 if the notification does not belong to the authenticated user.
POST
/users/me/notifications/read-all
Auth
Mark all unread notifications for the authenticated user as read. Returns { marked: number }.
DELETE
/users/me
Auth
Self-service account deletion. Soft-deletes the user, clears favorites and reminder preferences, removes sent pending suggestions, and keeps community timing submissions for continuity.
Schedules
Prayer schedules are derived state — a snapshot of a masjid's timings at a point in time, with a verification status (pending or verified) and a validity range. Most apps don't need these endpoints; /mosques/:id already includes the active schedule. Use these when you want history.
GET
/schedules
Public
Paginated list of schedules across masjids.
GET
/schedules/:id
Public
A single schedule with its verifier and contributor.
GET
/schedules/mosque/:mosqueId/active
Public
The currently active schedule for a masjid. 404 if the masjid has none.
Compliance
POST
/account-deletion-request
Public
1 / hr / email
Auth-less account deletion request for users who cannot access the app. The response does not reveal whether the email belongs to an account.
{
"email": "[email protected]",
"reason": "I can no longer access the app"
}