Back to writing

Sirat: A Route Through Time and Place

Building a route planner that brings together journeys, mosques and congregational prayer times across Britain.

9 min read

I had a problem. While travelling on the road, I wanted to catch the prayer at the mosque nearest to me. This isn’t as easy a task as you may think, though.

  1. Prayer times vary depending on where you are. You have to coordinate where you will be with the time you need to be there.
  2. Mosques don’t necessarily hold the congregational prayer at the prayer time. For convenience, they usually hold it at a pre-published time.

I usually had to do some trial and error to see which route would be best, which mosques were on the way and what time I could reach them. To solve this, I set out to make an app.

One Problem Became Two

Now I had two problems. The first was figuring out how to plot a route that intersects time and place at the right point. The second, which ended up being much harder, was finding the congregational prayer times in the first place.

For my app to work, I ideally needed a list of not only every mosque in the UK, but also all of their congregational times. There is no standard (yet) that each mosque uses; some keep a custom-formatted timetable on their website, some use an external provider and some just upload an image to Facebook every month.

Finding the Mosques

To find as many mosques as I could, I used mosques.muslimsinbritain.org, which maintains a UK-wide directory. May Allah reward them for their efforts. Without this source, the project would have been much more difficult.

Many of the directory’s listings contain a website. A good portion of those URLs, however, were old or dead. To work around this, I spawned some AI agents to find a URL for a given mosque and confirm that it was a match only if it met certain criteria, such as a matching name or postcode.

Another round of deterministic checks then fetched the page again and rejected anything that didn’t look like a mosque website. It threw out things like directories and social-media pages, since we usually can’t crawl them. If the evidence wasn’t good enough, the site was surfaced for human review.

At the time of writing, our private database has 2,074 mosque records, of which 1,073 have stored website URLs. Across the dataset, we’ve extracted timetables for 635 distinct mosques. Of those, 513 currently have upcoming published times. Most of the remainder are awaiting refresh, review or re-authoring.

Finding the Timetable

The first issue was finding where exactly the timetable lived. We searched common paths such as /prayer-times and /timetable. The pipeline also performs liveness and redirect checks, recognises known platforms and gathers the likely pages or endpoints. If it needs to render a page, it does so using Playwright.

Once we had found the timetable, we needed to extract it. These timetables generally fell into a few buckets:

  • Uploaded media (image/PDF)
  • HTML timetable
  • API (usually displayed as the above)
  • External provider (commonly MasjidBox, Mawaqit)

Going through every mosque site and extracting the times each month would be completely impractical. For a human, at least. For an AI, it looked trivial.

My first idea was to throw an agent at each site with this rough goal:

Find the timetable on this site. Store how the timetable is hosted. If it’s an image or PDF, record it for the vision model to read and extract. Otherwise, write a deterministic extractor config for this timetable.

In practice, this was going to cost a lot. I tried various budget models, but they often struggled or took too long. The easy answer was to throw more compute at the problem through stronger, and perhaps faster, models. That would have been extremely costly, though.

Teaching the Pipeline the Common Cases

To work around this, we built a set of rules common to a good chunk of the HTML timetables, so the system wasn’t writing a bespoke extractor for each website. The pipeline fetched the pages, then deterministically enumerated those rules to try to produce a config from the gathered evidence. Only the remaining cases fell back to bounded model authoring, then agentic browsing or a bespoke extractor if needed.

We also tuned the prompts being fed to the agents. Instead of making an agent rediscover candidate pages each time, we gave it the likely candidates upfront and made it focus there first.

On top of that, we didn’t simply trust the agent’s output. Each proposed config was replayed against the evidence and run through various validation gates before it was accepted:

  • Plausible prayer-time windows: Reject times that are implausible for that prayer in the UK, such as a Fajr prayer in the afternoon.
  • Ordered and distinct Jumu’ah sessions: Friday prayer may have multiple sessions, but they must be in chronological order.
  • Agreement with the source evidence: An ordinary extracted jamaat time must appear in the captured source page or media. This stops a config from hallucinating a time.
  • Begin-time-versus-jamaat-time handling: Many timetables show both the salah start/adhan time and the later jamaat/iqamah time. This gate checks that a jamaat time isn’t mapped as begin, or vice versa.
  • Known timetable conventions: We included conventions the model may not be able to infer, such as recognising “Sunset” as a Maghrib label. This isn’t exactly a validation gate, but it belongs in the same safety net.

Keeping It Fresh

Now, a scheduled job fires the extractors every day. This loop is mostly deterministic. It:

  1. Fetches the retained source
  2. Applies the retained config
  3. Normalises the data and updates the stored prayer times
  4. Runs the gates again

This is what handles websites that publish only today’s times.

The same refresh job scans pages for new monthly timetables, so it can stay in sync with each mosque’s newly published times. Images and PDFs are the exception. Their bytes are hashed, and their normalised grids are cached by hash and month.

Where a media source also has a stable parent page, the job re-resolves the current link (e.g. if it now needs to fetch /august.pdf instead of /july.pdf). Only new or changed media gets sent to the vision model with a bounded call budget, and the output still has to pass the same gates. The job runs a freshness sweep afterwards to identify sources being served with no future times.

The Sirat API

Once that part was done, the next thing to finish was the routing protocol. I explored some options and decided to self-host the maps and route planner. This was primarily a cost-saving decision. Google Maps and Mapbox (probably the best two paid offerings) would blow through the budget given the route matrix being constructed.

Sirat instead runs Valhalla with a Great Britain OpenStreetMap extract. It mirrors our Directory’s public snapshot into its own PostGIS database every six hours, using the snapshot ETag to avoid an unnecessary sync if nothing has changed. This means a user’s journey request never needs to hammer our Directory live.

A Route Through Time and Place

What do I mean by a route matrix?

The router first calculates one baseline route from A to B and works out which prayers fall within that journey. From there, it finds mosques inside a configurable corridor around the route.

The planner limits how many mosques it considers. It picks candidates from the start, middle and end of the route, so a busy area near the beginning doesn’t crowd out better options later on. If a long journey has too few options, it expands the search area once. If it still can’t find enough suitable mosque candidates, it tells the user instead of searching indefinitely.

Instead of plotting a full journey through every possible mosque, the planner first asks for a batch of travel-time estimates between the start, destination and likely mosque stops. It then tests which of those sequences actually work with the prayer times.

The planner ranks journeys according to these priorities:

  1. Cover the prayers due during the journey
  2. Prefer verified jamaat times over calculated times
  3. Minimise detour and waiting times

If a precise final route shows that the exact route is no longer reachable in time or adds too much of a detour, the planner drops it before returning the result.

Keeping the Router Lean

Keeping things self-hosted means we can not only stay within a small budget (that being primarily the cost of the VPS), but also fine-tune and optimise the router internals. Some of these optimisations include:

  • One corridor query, widening once: Sirat searches its local mosque database along the route for all prayers that could be due, rather than searching per mosque or per route leg. If a long route has too few candidates, it widens the search once.
  • Limited candidates and bulk travel estimates: It limits the number of mosques considered, then asks for many travel-time estimates in a small number of batches rather than plotting a full route for every possible mosque.
  • Reuse work and route finalists in parallel: Calculated prayer windows are cached instead of being recalculated repeatedly. Shortlisted plans are also fully routed at the same time instead of one after another.

One trade-off, however, is that we aren’t able to account for live or predicted traffic in the routing behaviour. It may be an area worth exploring again in the future. As a rough hedge against this, Sirat applies a conservative factor of 1.15 to both matrix and final-route durations.

On the Path

When it came to finding a name for this app, I couldn’t see anything that was a more perfect fit than Sirat. This app, Inshallah, will help you on the path to Allah, both literally and spiritually.

To help other Muslim developers, I have also made the Directory API public. It is listed publicly as Mosque Times by Sirat.

Inshallah, the ideal vision would be to standardise the way timetables are hosted. I know there are a few players in this market, and may Allah help them in their endeavours. In the meantime, though, I hope this is helpful.