https://github.com/decocms/fast-flights-ts
Port of fast_flights to TypeScript focused on performance and low dep count.
https://github.com/decocms/fast-flights-ts
Last synced: about 1 month ago
JSON representation
Port of fast_flights to TypeScript focused on performance and low dep count.
- Host: GitHub
- URL: https://github.com/decocms/fast-flights-ts
- Owner: decocms
- License: mit
- Created: 2026-04-12T22:28:09.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-04-13T01:41:05.000Z (3 months ago)
- Last Synced: 2026-04-13T02:25:06.299Z (3 months ago)
- Language: TypeScript
- Size: 944 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# fast-flights-ts
Fast, strongly-typed Google Flights scraper for Node.js.
Calls Google's internal RPC endpoint directly -- no HTML scraping, no browser required.
```bash
npm install fast-flights-ts
```
> Forked from [AWeirdDev/flights](https://github.com/AWeirdDev/flights) (Python).
> Ported to TypeScript by [Claude Opus 4.6](https://claude.ai).
## Quick start
```typescript
import { createQuery, Passengers, getFlights } from "fast-flights-ts";
const query = createQuery({
flights: [
{ date: "2026-05-25", from_airport: "GIG", to_airport: "SFO" },
],
seat: "economy",
trip: "one-way",
passengers: new Passengers({ adults: 1 }),
currency: "USD",
});
const results = await getFlights(query);
for (const flight of results) {
console.log(
flight.airlines.join(", "),
`$${flight.price}`,
flight.flights.map((f) => `${f.from_airport.code}->${f.to_airport.code}`).join(" "),
);
}
```
## Multi-city
Multi-city and open-jaw queries work out of the box:
```typescript
const query = createQuery({
flights: [
{ date: "2026-05-21", from_airport: "GIG", to_airport: "LAX" },
{ date: "2026-05-28", from_airport: "SFO", to_airport: "GIG" },
],
seat: "business",
trip: "multi-city",
passengers: new Passengers({ adults: 1 }),
});
const results = await getFlights(query);
```
## Options
`getFlights` and `fetchFlightsHtml` accept a second argument:
```typescript
const results = await getFlights(query, {
timeout: 15_000, // ms, default 30s
maxRetries: 3, // retry on 429/503/timeout, default 0
retryDelay: 2_000, // base delay with exponential backoff + jitter
signal: controller.signal, // external AbortSignal
debug: true, // log to console
});
```
## Integrations
### Bright Data
When using an integration, the library falls back to HTML scraping:
```typescript
import { getFlights, BrightData } from "fast-flights-ts";
const results = await getFlights(query, {
integration: new BrightData({ api_key: "your-key" }),
});
```
Env vars: `BRIGHT_DATA_API_KEY`, `BRIGHT_DATA_API_URL`, `BRIGHT_DATA_SERP_ZONE`.
## API
### `createQuery(options)`
| Option | Type | Default |
|--------|------|---------|
| `flights` | `FlightQueryInput[]` | required |
| `seat` | `"economy" \| "premium-economy" \| "business" \| "first"` | `"economy"` |
| `trip` | `"round-trip" \| "one-way" \| "multi-city"` | `"one-way"` |
| `passengers` | `Passengers` | 1 adult |
| `language` | `Language \| ""` | `""` |
| `currency` | `Currency \| ""` | `""` |
| `max_stops` | `number \| null` | `null` |
### `getFlights(query, options?)`
Returns `Promise` -- an array of `Flights` with attached `metadata`.
### `Passengers`
```typescript
new Passengers({ adults: 2, children: 1, infants_in_seat: 0, infants_on_lap: 0 })
```
Max 9 total. Infants on lap cannot exceed adult count.
## Error handling
All errors extend `FlightError`:
```typescript
import { FlightError, HttpError, CaptchaError, TimeoutError, ParseError } from "fast-flights-ts";
try {
await getFlights(query);
} catch (e) {
if (e instanceof TimeoutError) { /* request timed out */ }
if (e instanceof HttpError) { /* e.status, e.responseLength */ }
if (e instanceof CaptchaError) { /* Google wants a CAPTCHA */ }
if (e instanceof ParseError) { /* unexpected response format */ }
}
```
Retry is automatic for 429, 502, 503, 504, and timeouts when `maxRetries > 0`.
## Response types
```typescript
interface Flights {
type: string;
price: number;
airlines: string[];
flights: SingleFlight[];
carbon: CarbonEmission;
}
interface SingleFlight {
from_airport: Airport; // { code, name }
to_airport: Airport;
departure: SimpleDatetime;
arrival: SimpleDatetime;
duration: number; // minutes
plane_type: string;
}
interface FlightResults extends Array {
metadata: JsMetadata; // { airlines, alliances }
}
```
## Benchmarks
Compared against the original [Python implementation](https://github.com/AWeirdDev/flights) v3 (10,000 iterations, `bash bench/cross-lang.sh`).
### Encoding (query + protobuf + base64)
| | ops/sec | latency |
|-|---------|---------|
| Python v3 | 214,626 | 4.7 us |
| **TypeScript** | **462,181** | **2.2 us** |
**2.2x faster.**
### Parsing (20-flight payload, JSON extract + mapping)
| | ops/sec | latency |
|-|---------|---------|
| Python v3 | 17,711 | 56.5 us |
| **TypeScript** | **53,911** | **18.5 us** |
**3.0x faster.**
### Detailed breakdown (`npm run bench`)
| Operation | ops/sec | mean |
|-----------|---------|------|
| `encodeInfo` (simple) | 833,718 | 1.2 us |
| `encodeInfo` (complex) | 341,613 | 2.9 us |
| `encodeInfoToBase64` | 768,435 | 1.3 us |
| `createQuery` | 16,330,189 | 0.06 us |
| `createQuery` + `toStr()` | 728,724 | 1.4 us |
| `parseRpcResponse` (5 flights) | 151,354 | 6.6 us |
| `parseRpcResponse` (50 flights) | 17,604 | 56.8 us |
| `parseRpcResponse` (200 flights) | 4,368 | 229 us |
## How it works
1. **Query** -- builds flight parameters (dates, airports, seat, passengers)
2. **RPC** -- POSTs directly to Google's `GetShoppingResults` endpoint
3. **Parse** -- strips `)]}'` prefix, extracts nested JSON with flight data
4. **Fallback** -- HTML scraping via `node-libcurl` or `fetch` when using integrations
The RPC approach is used by default for structured queries. It works for all trip types (one-way, round-trip, multi-city) and returns more results than HTML scraping (best + other flights).
### Why it's fast
- **Direct RPC** -- no HTML to download or parse, ~35KB response vs ~2MB HTML
- **Manual protobuf encoder** (~80 lines, no protobufjs)
- **Zero runtime dependencies** -- `node-libcurl` is optional
- **20 KB** bundled (ESM + CJS dual output)
## Development
```bash
npm install
npm run typecheck
npm test # 21 tests
npm run bench
npm run build
```
## License
MIT -- see [LICENSE](LICENSE).
Original Python library by [AWeirdDev](https://github.com/AWeirdDev).
RPC approach inspired by [swoop](https://github.com/saraswatayu/swoop).