Tracking the Ramadan ticket rush on Turkish intercity buses

这篇内容暂时只有 English 版本,以下是原文。

Two weeks before Ramazan Bayramı, the İstanbul–Nevşehir night bus I was watching went from roughly 40% occupancy to sold out in under 36 hours. I had been logging seat availability on the Metro Turizm and Süha Turizm sites twice a day, plus the Obilet aggregator, partly out of personal need and partly to see how the holiday surge actually moves through the system. Over eight days the pattern was consistent: the first departures to fill were the 22:00–01:00 overnight runs from Esenler and Alibeyköy, then the morning services, and only then the midday ones. Prices on the same route rose by about 25% between my first and last snapshot, and the cheapest seats disappeared before the convenient ones.

Here is the small script I used to snapshot availability. It is deliberately dumb — one request every few hours, no parallel hammering, because these booking sites will rate-limit or quietly serve you stale seat maps if you push too hard:

import requests, time, csv
from datetime import datetime

URL = "https://www.obilet.com/api/busjourney/search"
PARAMS = {
    "origin": "349",      # Esenler Otogarı id, check yours
    "destination": "410", # Nevşehir id
    "date": "2025-03-28",
}
HEADERS = {"User-Agent": "Mozilla/5.0 (personal availability check)"}

with open("seats.csv", "a", newline="") as f:
    w = csv.writer(f)
    w.writerow(["ts", "departure", "operator", "seats_left", "price_try"])
    r = requests.get(URL, params=PARAMS, headers=HEADERS, timeout=20)
    r.raise_for_status()
    for j in r.json().get("journeys", []):
        w.writerow([datetime.utcnow().isoformat(),
                    j["departure"], j["operator"],
                    j.get("emptySeat"), j.get("price")])
    time.sleep(2)

I ran it manually via cron every four hours, not every minute. The mechanism behind the surge is not mysterious: holiday demand is concentrated on a handful of long-distance corridors, operators release seats in blocks, and the aggregator caches results for a few minutes. So a seat you see as available may already be gone, and a seat you see as gone may reappear when a block is released or a reservation expires. That is why I logged rather than trusted a single reading.

The cost is time and fragility. The endpoint ids are undocumented and will change; the JSON field names shift between operators; and if you poll aggressively you get 429s or, worse, a 200 with an empty journey list that looks like a sold-out bus. I lost two days of data that way before I added a sanity check on the response length. The method also fails for routes where the operator sells only through its own app and never exposes inventory to aggregators — I could not track the small Nevşehir cooperatives at all. And a sold-out snapshot tells you nothing about cancellations unless you keep polling after the fact.

What I still cannot explain: why the 23:30 departure from Alibeyköy filled before the 22:00 from Esenler on the same night, when the latter is the more familiar terminal for most travellers. If you have watched bayram seats on your own route, which departure time went first?

0
赞同度 100% · 0 条评论 · 5 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。