Find Buser bus departures for a specified origin, destination, and date. Both the seat-type filter and the departure-time window are caller-supplied and default to no filter, so the unfiltered call returns every rendered departure.
Use Cases
Use when the caller provides Brazilian cities or route slugs and a travel date. The caller may additionally request a seat type such as leito or semi-leito, and a departure-time window such as overnight or morning departures. The route-results page can be reached directly without visiting the homepage or operating the date-picker UI.
Automation Flow
- Build the route URL with Buser's lowercase city/state slugs:
https://www.buser.com.br/onibus/{origin-city-slug}-{origin-state}/{destination-city-slug}-{destination-state}?ida={YYYY-MM-DD}
2. In one browser call, goto that URL with waitUntil: "domcontentloaded", allow the client-rendered results to settle briefly if necessary, and run this evaluate() extractor on the loaded page. Set seatTypePattern, windowStartHour, and windowEndHour according to the request; leave them null to return every departure:
(() => {
const seatTypePattern=null,windowStartHour=null,windowEndHour=null;
let seatRe=null;try{seatRe=seatTypePattern?new RegExp(seatTypePattern,'i'):null}catch{}
const hasWindow = Number.isInteger(windowStartHour) && Number.isInteger(windowEndHour);
const inWindow = h => windowStartHour <= windowEndHour
? h >= windowStartHour && h <= windowEndHour
: h >= windowStartHour || h <= windowEndHour;
const visible = el => {
const s = getComputedStyle(el);
return s.display !== 'none' && s.visibility !== 'hidden' && el.getBoundingClientRect().width > 0;
};
const cards = new Map();
const roots = seatRe ? [...document.querySelectorAll('body *')] : [...document.querySelectorAll('article, li, [role="article"], [data-testid*="trip"], [data-testid*="offer"], [data-testid*="card"]')];
for (const el of roots) {
if (!visible(el)) continue;
if (seatRe && !seatRe.test((el.textContent || '').replace(/\s+/g, ' ').trim())) continue;
const card = el.closest('article, li, [role="article"], [data-testid*="trip"], [data-testid*="offer"], [data-testid*="card"]') || el.parentElement;
if (!card || !visible(card)) continue;
const text = (card.textContent || '').replace(/\s+/g, ' ').trim();
if (!text || text.length > 2500) continue;
if (seatRe && !seatRe.test(text)) continue;
const times = [...text.matchAll(/\b(?:[01]\d|2[0-3]):[0-5]\d\b/g)].map(m => m[0]);
if (!times.length) continue;
const href = card.querySelector('a[href]')?.href || null;
const key = `${text}|${href || ''}`;
cards.set(key, {text, href, times, inWindow: hasWindow ? inWindow(Number(times[0].slice(0, 2))) : null});
}
const all = [...cards.values()];
return {url: location.href, seatTypePattern, windowStartHour, windowEndHour, departures: hasWindow ? all.filter(x => x.inWindow) : all};
})()- If the page uses a different rendered card ancestor, inspect only the current page and extend the ancestor selector; do not navigate through the homepage.
Possible Friction Points
- The direct results route uses city/state slugs under
/onibus/; the observed date query parameter that reaches the final state isida={YYYY-MM-DD}.data_ida={YYYY-MM-DD}was also observed as an equivalent route parameter, but preferida. - Results are client-rendered after navigation, so extraction may need a short settle delay after
domcontentloaded. - Worked example, overnight sleeper buses:
seatTypePatternof\bleito(?:-cama)?\bmatches bothleitoandleito-camacase-insensitively, andwindowStartHour=18withwindowEndHour=5selects departures from 18:00 through 05:59. The window wraps past midnight when the start hour is greater than the end hour. windowStartHour/windowEndHourfilter on the first clock time in the card, which is the departure time in the observed layout. Verify that ordering before trusting the filter if Buser changes its card markup.- Do not bake a seat type or time window into the extractor. The filters are caller inputs; the extractor returns the rendered card text and link rather than assuming a fragile single CSS class.
- Neighborhoods or arrival areas such as Flamengo are not represented in the observed route URL; use the city route and report the rendered destination details when available.