Skip to the text

Developers · Reference

Mass-times API

A worldwide directory of churches and the times of their Masses, confessions and adoration. Reads are open to anyone; a correction is a suggestion that a person reviews.

Give this to your agent ember.dpgu.me/llms.txt · this page in Markdown

Basics

Base URL
https://ember-mass-times.dpgu.workers.dev
OpenAPI
openapi.json, for Postman, Bruno or a client generator
Auth
None. Every route allows any origin, so a web page can call it directly.
Cache
Reads are public, max-age=300; map tiles an hour, then a day stale while revalidating.
Errors
A missing church is 404 { "error": "not_found" }. A bad parameter is 400 with the validation issues. Too many writes is 429 { "error": "rate_limited" }.

Reading a schedule

A church’s services are its recurring times, one pattern at one time each: “Sundays at 9:00” is one service and “Sundays at 11:30” another. rrule is an iCalendar rule that picks the days, and startTime gives the wall-clock time in the church’s own timezone. exdate lists cancelled days and rdate added ones. The API sends the rules unexpanded, so you compute the next Mass on your side.

expand.ts does this in sixty lines on the rrule package, and like the rest of Ember’s code it is in the public domain to copy:

JavaScript
import { expandService } from './expand' // packages/api/src/expand.ts

const church = await (await fetch('https://ember-mass-times.dpgu.workers.dev/churches/ns-do-carmo-se-sao-paulo-sp')).json()
for (const service of church.services.filter((s) => s.kind === 'mass')) {
  for (const { date, startTime } of expandService(service, { count: 3 })) {
    console.log(date.toISOString().slice(0, 10), startTime, service.language)
  }
}

A bundler takes it as it is. Plain Node finds no named exports in rrule: write import rrule from 'rrule' and const { RRule, RRuleSet } = rrule.

Where no structured schedule is on record (hasStructuredSchedule is false), the parish’s own wording is in texts, as it was published.

Churches

Public reads

GET /churches/near

Churches near a point

Churches within radiusKm of a point, nearest first. A kind or rite filter trims each church’s services and drops churches left with none.

Parameters
lat query required number
lng query required number
radiusKm query number
kind query string

Keep only services of this kind: mass, confession, adoration

rite query string

Keep only services of this rite, e.g. latin_tridentine

institute query string

Only churches served by this institute, e.g. FSSP

status query string

active | temporarily_closed | closed

limit query integer

Responses

  • 200 OK { churches: (Church & { distanceKm: number })[] }
  • 400 A parameter or the body failed validation ValidationError

Try it

https://ember-mass-times.dpgu.workers.dev/churches/near?lat=-23.5505&lng=-46.6333&radiusKm=2&limit=5
GET /churches

Search by name, or read a map viewport

Give q to search by name, or bbox for the churches in a map viewport. A viewport too wide to list answers with clusters, counted by geohash cell.

Parameters
q query string

Name search

near query string

With q: "lat,lng" to rank matches nearest it first

bbox query string

A map viewport, "minLng,minLat,maxLng,maxLat": churches, or clusters when wide

kind query string
rite query string
institute query string
status query string
limit query integer
offset query integer

Responses

  • 200 { churches } for a search, { churches, clusters } for a viewport { churches: Church[], clusters?: Cluster[] }
  • 400 A parameter or the body failed validation ValidationError

Try it

https://ember-mass-times.dpgu.workers.dev/churches?q=Carmo&near=-23.5505%2C-46.6333&limit=5
GET /churches/index

The whole directory, a page at a time

Every church in id order, with the few fields a sitemap needs, or with full=1 each church whole, as /churches/{id} gives it. Pass the next of one page as the after of the next; a page without next is the last.

Parameters
after query string

The last id of the previous page

scheduled query "1"

Only churches with service times on record

country query string

Only churches in these countries: ISO codes, comma-separated (BR,PT)

limit query integer
full query "1"

Each church whole, as /churches/{id} gives it: schedule, texts, links, parish and chapels

Responses

  • 200 OK { churches: { id: string, name: string, longName: string | null, city: string | null, region: string | null, countryCode: string | null, hasStructuredSchedule: boolean | null, updatedAt: string | null }[], next?: string }
  • 400 A parameter or the body failed validation ValidationError

Try it

https://ember-mass-times.dpgu.workers.dev/churches/index?limit=5
GET /churches/tiles/{cell}

A map tile

A five-character cell answers with its churches (without texts and links); a shorter one, or root, with the counts of the cells inside it. Cached for an hour.

Parameters
cell path required string

A geohash of 1–5 characters, or root

kind query string

Count only churches with services of this kind

Responses

  • 200 { churches } for a full-length cell, { cells } above it { churches: Church[] } | { cells: Cluster[] }
  • 400 A parameter or the body failed validation ValidationError

Try it

https://ember-mass-times.dpgu.workers.dev/churches/tiles/6gyf
GET /churches/{id}

One church

A church with its schedule, texts and links, the parish it belongs to and the chapels it serves.

Parameters
id path required string

The church id, a slug of its name and city

Responses

Try it

https://ember-mass-times.dpgu.workers.dev/churches/ns-do-carmo-se-sao-paulo-sp
GET /churches/{id}/verifications

Confirmations of a church’s times

When people confirmed the times on record, newest first.

Parameters
id path required string

The church id, a slug of its name and city

limit query integer
offset query integer

Responses

Try it

https://ember-mass-times.dpgu.workers.dev/churches/ns-do-carmo-se-sao-paulo-sp/verifications?limit=5

Changes

What the moderators changed, for directories that keep in step

GET /changes

The change log

Every change a moderator applied to the directory and that still stands, newest first. Each church it touched comes with what changed and its schedule as it now stands, and message says the same as plain text, ready to pass on. Pass the next of one page as the before of the next; to keep in step, ask with since set to the newest appliedAt you hold.

Recent changes shows the same log as a page.

Parameters
since query string

Only changes applied after this instant (ISO 8601)

before query string

The last id of the previous page

church query string

Only changes that touched this church

lang query "en" | "pt"

The language of every message; by default each church’s own

limit query integer

Responses

  • 200 OK { changes: { id: string, appliedAt: string, churches: ChangedChurch[], message: string }[], next?: string }
  • 400 A parameter or the body failed validation ValidationError

Try it

https://ember-mass-times.dpgu.workers.dev/changes?limit=5

Suggestions

Rate-limited writes, reviewed before they apply

POST /churches/{id}/verify

Confirm the times on record

Counts once per client and church in 30 days; a repeat answers deduped: true and records nothing.

Parameters
id path required string

The church id, a slug of its name and city

X-Client-Id header string

A stable id per device or person; with the IP, it identifies the client for the rate limit and for confirmations

Request body

serviceId string

The service confirmed; none confirms the whole church

Responses

  • 200 OK { deduped: boolean }
  • 400 A parameter or the body failed validation ValidationError
  • 429 Too many writes from this client: rate_limited Error
Shell
curl -X POST 'https://ember-mass-times.dpgu.workers.dev/churches/ns-do-carmo-se-sao-paulo-sp/verify' \
  -H 'Content-Type: application/json' -H 'X-Client-Id: device-7f3a2c' \
  -d '{}'
POST /churches/{id}/corrections

Suggest a correction

Queues a change to a service or to the church, a new service, a closure, or a note, for a moderator. kind decides the shape of the rest.

Parameters
id path required string

The church id, a slug of its name and city

X-Client-Id header string

A stable id per device or person; with the IP, it identifies the client for the rate limit and for confirmations

Request body

kind: "edit_service"

kind required "edit_service"
serviceId required string
changes required object
↳ kind string
↳ rite string
↳ language string
↳ rrule string
↳ startTime string
↳ endTime string
↳ exdate string
↳ rdate string
↳ locationNote string
↳ note string
comment string at least 1 charactersat most 2000 characters
attachmentKeys string[] at most 10 items

kind: "edit_church"

kind required "edit_church"
changes required object
↳ name string
↳ longName string
↳ address string
↳ city string
↳ region string
↳ postalCode string
↳ country string
↳ phoneE164 string
↳ email string
↳ status string
↳ administration string
↳ institute string
↳ canonicalStatus string
↳ note string
comment string at least 1 charactersat most 2000 characters
attachmentKeys string[] at most 10 items

kind: "add_service"

kind required "add_service"
changes required object
↳ kind required string
↳ rite string
↳ language string
↳ rrule required string
↳ startTime required string
↳ endTime string
↳ exdate string
↳ rdate string
↳ locationNote string
↳ note string
comment string at least 1 charactersat most 2000 characters
attachmentKeys string[] at most 10 items

kind: "flag_closed"

kind required "flag_closed"
serviceId string
comment string at least 1 charactersat most 2000 characters
attachmentKeys string[] at most 10 items

kind: "note"

kind required "note"
serviceId string
comment required string at least 1 charactersat most 2000 characters
attachmentKeys string[] at most 10 items

Responses

  • 201 Queued { id: string }
  • 400 A parameter or the body failed validation ValidationError
  • 429 Too many writes from this client: rate_limited Error
Shell
curl -X POST 'https://ember-mass-times.dpgu.workers.dev/churches/ns-do-carmo-se-sao-paulo-sp/corrections' \
  -H 'Content-Type: application/json' -H 'X-Client-Id: device-7f3a2c' \
  -d '{"kind": "note", "comment": "From October the Sunday Mass is at 9:30."}'
POST /churches/{id}/corrections/attachments

Attach a photo to a correction

The raw bytes of one file of at most 1 MB, such as a photo of the bulletin. Send the returned key in the correction’s attachmentKeys.

Parameters
id path required string

The church id, a slug of its name and city

X-Client-Id header string

A stable id per device or person; with the IP, it identifies the client for the rate limit and for confirmations

Request body

Responses

  • 201 Stored { key: string }
  • 400 An empty body: empty_body Error
  • 413 Larger than 1 MB: too_large Error
  • 429 Too many writes from this client: rate_limited Error
Shell
curl -X POST 'https://ember-mass-times.dpgu.workers.dev/churches/ns-do-carmo-se-sao-paulo-sp/corrections/attachments' \
  -H 'Content-Type: image/jpeg' -H 'X-Client-Id: device-7f3a2c' \
  --data-binary @bulletin.jpg
GET /health

Liveness

Responses

  • 200 OK { ok: boolean }

Schemas

Church

id required string
name required string
longName required string | null
address required string | null
city required string | null
region required string | null
postalCode required string | null
country required string | null
countryCode required string | null
lat required number
lng required number
geohash required string
timezone required string
phoneE164 required string | null
email required string | null
status required string | null
featured required boolean | null
administration required string | null
institute required string | null
canonicalStatus required string | null
parishId required string | null
note required string | null
hasStructuredSchedule required boolean | null
lastVerifiedAt required string | null
verifiedSource required string | null
updatedAt required string | null
services required Service[] | null
texts required ChurchText[] | null
links required ChurchLink[] | null

Service

id required string
kind required string

mass | confession | adoration

rite string

Mass only: latin_novus_ordo | latin_tridentine | byzantine | …

language string

ISO 639

rrule required string

iCal RRULE: one pattern at one time

startTime required string

'HH:MM' wall-clock in the church's timezone

endTime string
exdate string

RRuleSet EXDATE: cancelled dates

rdate string

RRuleSet RDATE: one-off added dates

locationNote string
note string
source string

import | manual | user

confidence number

ChurchText

kind required string

mass_times | seasonal_mass_times | confession | adoration | info

rawText required string
sourceUpdatedAt string

ChurchKin

id required string
name required string

Cluster

id required string

The geohash cell

lat required number
lng required number
count required integer
church ChurchKin

Named when the cell holds one church

Verification

id required string
churchId required string
serviceId required string | null
fingerprint required string
createdAt required string

ChangeLine

label required string
before string
after string

ChangedChurch

id required string
name required string
isNew required boolean

Created by this change

parish ChurchKin

The parish this church is a chapel of

place required string
lat required number
lng required number
changes required ChangeLine[]
schedule required { kind: string, label: string, times: string[] }[]

Error

error required string

ValidationError

success: false

success required false
error required object
↳ name required "ZodError"
↳ message required string