agentic-google-flights-tool 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. agentic_google_flights_tool-0.1.0/.github/workflows/publish.yml +39 -0
  2. agentic_google_flights_tool-0.1.0/.gitignore +10 -0
  3. agentic_google_flights_tool-0.1.0/CHANGELOG.md +10 -0
  4. agentic_google_flights_tool-0.1.0/CONTRIBUTING.md +15 -0
  5. agentic_google_flights_tool-0.1.0/LICENSE +21 -0
  6. agentic_google_flights_tool-0.1.0/PKG-INFO +79 -0
  7. agentic_google_flights_tool-0.1.0/README.md +51 -0
  8. agentic_google_flights_tool-0.1.0/SECURITY.md +13 -0
  9. agentic_google_flights_tool-0.1.0/docs/agent-guide.md +44 -0
  10. agentic_google_flights_tool-0.1.0/docs/reference.md +152 -0
  11. agentic_google_flights_tool-0.1.0/examples/branching-combinations-live.json +52 -0
  12. agentic_google_flights_tool-0.1.0/examples/complete-tickets-live.json +46 -0
  13. agentic_google_flights_tool-0.1.0/examples/open-jaw-branching-live.json +33 -0
  14. agentic_google_flights_tool-0.1.0/examples/retrieval-filtering-live.json +63 -0
  15. agentic_google_flights_tool-0.1.0/examples/round-trip-outbound-choice.json +22 -0
  16. agentic_google_flights_tool-0.1.0/examples/search-one.json +21 -0
  17. agentic_google_flights_tool-0.1.0/examples/searches-50.json +658 -0
  18. agentic_google_flights_tool-0.1.0/examples/shortlist-filters.json +16 -0
  19. agentic_google_flights_tool-0.1.0/pyproject.toml +63 -0
  20. agentic_google_flights_tool-0.1.0/scripts/refresh_example_dates.py +69 -0
  21. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/__init__.py +41 -0
  22. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/batch.py +178 -0
  23. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/cache.py +90 -0
  24. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/cli.py +283 -0
  25. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/exploration.py +150 -0
  26. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/filtering.py +208 -0
  27. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/models.py +324 -0
  28. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/provider.py +1084 -0
  29. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/store.py +185 -0
  30. agentic_google_flights_tool-0.1.0/src/reverse_google_flights/views.py +230 -0
  31. agentic_google_flights_tool-0.1.0/tests/conftest.py +62 -0
  32. agentic_google_flights_tool-0.1.0/tests/test_batch.py +155 -0
  33. agentic_google_flights_tool-0.1.0/tests/test_cache.py +70 -0
  34. agentic_google_flights_tool-0.1.0/tests/test_cli.py +41 -0
  35. agentic_google_flights_tool-0.1.0/tests/test_exploration.py +143 -0
  36. agentic_google_flights_tool-0.1.0/tests/test_filtering.py +100 -0
  37. agentic_google_flights_tool-0.1.0/tests/test_guide.py +19 -0
  38. agentic_google_flights_tool-0.1.0/tests/test_models.py +80 -0
  39. agentic_google_flights_tool-0.1.0/tests/test_provider.py +310 -0
  40. agentic_google_flights_tool-0.1.0/tests/test_retrieval.py +141 -0
  41. agentic_google_flights_tool-0.1.0/tests/test_search_space.py +104 -0
  42. agentic_google_flights_tool-0.1.0/tests/test_store.py +138 -0
  43. agentic_google_flights_tool-0.1.0/tests/test_views.py +144 -0
  44. agentic_google_flights_tool-0.1.0/uv.lock +827 -0
@@ -0,0 +1,39 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ jobs:
10
+ build:
11
+ if: github.ref == 'refs/heads/main'
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-python@v5
16
+ with:
17
+ python-version: '3.11'
18
+ - run: python -m pip install build twine
19
+ - run: python -m build
20
+ - run: python -m twine check dist/*
21
+ - uses: actions/upload-artifact@v4
22
+ with:
23
+ name: distributions
24
+ path: dist/
25
+
26
+ publish:
27
+ needs: build
28
+ runs-on: ubuntu-latest
29
+ environment:
30
+ name: pypi
31
+ url: https://pypi.org/project/agentic-google-flights-tool/
32
+ permissions:
33
+ id-token: write
34
+ steps:
35
+ - uses: actions/download-artifact@v4
36
+ with:
37
+ name: distributions
38
+ path: dist/
39
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,10 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ **/__pycache__/
5
+ artifacts/
6
+ .coverage
7
+ .DS_Store
8
+ build/
9
+ dist/
10
+ *.egg-info/
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ This project uses semantic versioning once releases begin.
4
+
5
+ ## 0.1.0
6
+
7
+ - Added bounded batch search through browser automation.
8
+ - Added round-trip and multi-city ticket verification.
9
+ - Added caching, local filtering, ranking, and progressive result views.
10
+ - Added structured coverage and error reporting.
@@ -0,0 +1,15 @@
1
+ # Contributing
2
+
3
+ This is a small, best-effort project. Focused bug reports and pull requests are welcome, but response times may vary.
4
+
5
+ Before starting a large change, open an issue to check whether it fits the project. For a code change:
6
+
7
+ 1. Use Python 3.11 or newer.
8
+ 2. Install the development environment with `uv sync --extra dev`.
9
+ 3. Add or update tests for changed behavior.
10
+ 4. Run `uv run pytest` and `uv run ruff check .`.
11
+ 5. Keep generated search results, browser caches, and personal travel data out of commits.
12
+
13
+ Tests should be deterministic by default. Mark tests that contact Google with `pytest.mark.live` and run them only when the external request is the point of the test.
14
+
15
+ By contributing, you agree that your contribution will be licensed under the MIT License.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alberto Tamez Gonzalez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.5
2
+ Name: agentic-google-flights-tool
3
+ Version: 0.1.0
4
+ Summary: Explore and compare flight options with AI agents
5
+ Project-URL: Homepage, https://github.com/alberto-tamez/agentic-google-flights-tool
6
+ Project-URL: Repository, https://github.com/alberto-tamez/agentic-google-flights-tool
7
+ Project-URL: Issues, https://github.com/alberto-tamez/agentic-google-flights-tool/issues
8
+ Author: Alberto Tamez Gonzalez
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: browser-automation,flights,google-flights,travel
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Topic :: Internet :: WWW/HTTP :: Browsers
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: fast-flights==3.1.0
20
+ Requires-Dist: playwright<2,>=1.62
21
+ Requires-Dist: pydantic<3,>=2.10
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest<9,>=8.3; extra == 'dev'
24
+ Requires-Dist: ruff>=0.8; extra == 'dev'
25
+ Provides-Extra: fli
26
+ Requires-Dist: flights==0.9.0; extra == 'fli'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Agentic Flights
30
+
31
+ Find the best flights for your trip with your AI. Compare flexible dates, nearby airports, and trip lengths, then get a shortlist of the options worth booking.
32
+
33
+ ## Give this to your AI
34
+
35
+ Copy this into an AI agent with terminal access and add your trip details:
36
+
37
+ ```text
38
+ Install and use Agentic Flights from
39
+ https://github.com/alberto-tamez/agentic-google-flights-tool.
40
+ Follow its README and the bundled agent guide to find the best flights for my trip.
41
+
42
+ My trip: [departure city, destination, dates or flexibility, travelers,
43
+ and preferences].
44
+ ```
45
+
46
+ For example: “Madrid or Barcelona to Japan for about two weeks in October 2027. One adult, an overhead cabin bag, and at most one stop. I'd like a good balance of price and travel time.”
47
+
48
+ ## Install
49
+
50
+ Requires Python 3.11 or newer. Install the Python package from GitHub:
51
+
52
+ ```sh
53
+ python -m pip install "git+https://github.com/alberto-tamez/agentic-google-flights-tool.git"
54
+ ```
55
+
56
+ Then read the included instructions:
57
+
58
+ ```sh
59
+ agentic-flights guide
60
+ ```
61
+
62
+ The tool uses Google Chrome if installed. Otherwise:
63
+
64
+ ```sh
65
+ python -m playwright install chromium
66
+ ```
67
+
68
+ ## For agents
69
+
70
+ Start with `agentic-flights guide`. Load `agentic-flights guide reference` or
71
+ `agentic-flights guide schema` only when needed. Explore and compare results in
72
+ code, keep full reports outside the conversation, and verify the strongest
73
+ options before recommending them.
74
+
75
+ The package includes a Python API and CLI. An MCP server is not included yet.
76
+ Search coverage is limited by what Google returns, and fares can change.
77
+ This unofficial project is not affiliated with Google.
78
+
79
+ [Agent guide](docs/agent-guide.md) · [Technical reference](docs/reference.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md)
@@ -0,0 +1,51 @@
1
+ # Agentic Flights
2
+
3
+ Find the best flights for your trip with your AI. Compare flexible dates, nearby airports, and trip lengths, then get a shortlist of the options worth booking.
4
+
5
+ ## Give this to your AI
6
+
7
+ Copy this into an AI agent with terminal access and add your trip details:
8
+
9
+ ```text
10
+ Install and use Agentic Flights from
11
+ https://github.com/alberto-tamez/agentic-google-flights-tool.
12
+ Follow its README and the bundled agent guide to find the best flights for my trip.
13
+
14
+ My trip: [departure city, destination, dates or flexibility, travelers,
15
+ and preferences].
16
+ ```
17
+
18
+ For example: “Madrid or Barcelona to Japan for about two weeks in October 2027. One adult, an overhead cabin bag, and at most one stop. I'd like a good balance of price and travel time.”
19
+
20
+ ## Install
21
+
22
+ Requires Python 3.11 or newer. Install the Python package from GitHub:
23
+
24
+ ```sh
25
+ python -m pip install "git+https://github.com/alberto-tamez/agentic-google-flights-tool.git"
26
+ ```
27
+
28
+ Then read the included instructions:
29
+
30
+ ```sh
31
+ agentic-flights guide
32
+ ```
33
+
34
+ The tool uses Google Chrome if installed. Otherwise:
35
+
36
+ ```sh
37
+ python -m playwright install chromium
38
+ ```
39
+
40
+ ## For agents
41
+
42
+ Start with `agentic-flights guide`. Load `agentic-flights guide reference` or
43
+ `agentic-flights guide schema` only when needed. Explore and compare results in
44
+ code, keep full reports outside the conversation, and verify the strongest
45
+ options before recommending them.
46
+
47
+ The package includes a Python API and CLI. An MCP server is not included yet.
48
+ Search coverage is limited by what Google returns, and fares can change.
49
+ This unofficial project is not affiliated with Google.
50
+
51
+ [Agent guide](docs/agent-guide.md) · [Technical reference](docs/reference.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md)
@@ -0,0 +1,13 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Only the latest version on the default branch receives security fixes. This project is maintained on a best-effort basis and does not promise a response or release schedule.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Do not include credentials, private booking links, personal travel details, or a working exploit in a public issue.
10
+
11
+ Use GitHub's private vulnerability reporting for this repository if it is available. If it is not available, open a public issue that contains only a short request for a private contact channel. Include enough information to distinguish a security report from a normal bug, but omit sensitive details.
12
+
13
+ Reports about changes to Google Flights markup or undocumented responses are ordinary compatibility bugs unless they expose private data or create a security boundary failure.
@@ -0,0 +1,44 @@
1
+ # Agentic Flights: agent guide
2
+
3
+ Use this tool to answer a trip-planning question with a small, useful shortlist. Run searches and comparisons in code; return only the evidence the user needs to choose.
4
+
5
+ ## Setup and instructions
6
+
7
+ After installing the package, run `agentic-flights guide` from any directory.
8
+ Use an installed Google Chrome, or run `python -m playwright install chromium`.
9
+ Read `agentic-flights guide reference` for Python examples and detailed fields;
10
+ read `agentic-flights guide schema` for the JSON input schema. These instructions
11
+ are bundled with the installed version; no repository checkout is required.
12
+
13
+ ## Turn the goal into a search
14
+
15
+ Identify origin and destination airport choices, departure dates, trip lengths, passengers, currency, and hard requirements. Resolve relative dates to concrete dates and cities to explicit airport codes. Clarify only missing information that changes the search.
16
+
17
+ For flexible one-way or round-trip searches, use `SearchSpace` and check `space.count` before executing. Choose reasonable internal batch sizes and continue exploring relevant alternatives. Respect any explicit user budget, but do not require users to specify a combination count. If the search is too broad to complete, narrow it using the trip preferences and explain the remaining uncertainty. For exact or multi-city itineraries, supply `SearchSpec` requests to `BatchExecutor` or the CLI.
18
+
19
+ The [Python example](reference.md#programmatic-trip-exploration) shows the API setup. Read it when implementing the search; consult the rest of the reference only for fields you need.
20
+
21
+ ## Explore, compare, verify
22
+
23
+ 1. Start with `search_mode="discover"` and apply the user's price, stop, and timing constraints. Keep browser concurrency at two workers.
24
+ 2. Execute bounded batches. With `Exploration.advance`, save the returned `run_id` and pass it as `resume_from` to continue the same space. Continue through the relevant search space, respecting any explicit user budget. Failures count as attempted; inspect them before claiming coverage.
25
+ 3. Filter and rank saved results in code. Compare prices within the same currency and preserve useful price-versus-duration choices. Keep full result JSON outside model context.
26
+ 4. Run `search_mode="verify"` for the shortlisted trip queries that need complete prices or baggage evidence. Verification searches current inventory again; it does not lock a discovery result. Never present provisional multi-leg prices or summed one-way fares as a verified complete ticket.
27
+
28
+ ## Read only what you need
29
+
30
+ Run these commands from any directory, replacing the IDs with returned values:
31
+
32
+ ```sh
33
+ agentic-flights summary RUN_ID
34
+ agentic-flights list RUN_ID --page-size 5
35
+ agentic-flights show RUN_ID RESULT_ID
36
+ ```
37
+
38
+ Use `list --filters` to narrow results offline. Expand only finalists with `show`; it includes fare evidence and booking URLs. Avoid `--full` in conversational tool output. Saved runs can expire; export results the user needs to keep.
39
+
40
+ ## Return a decision
41
+
42
+ Present a short comparison with total price and currency, dates, airports, duration, stops, and verified baggage where relevant. Explain why each option is worth considering. Include booking links when available and distinguish verified totals from provisional prices.
43
+
44
+ State how many combinations were attempted, failures, remaining queries, and any retrieval limits that affect the recommendation. Exhausting the defined search space does not prove the global lowest fare. Do not book tickets.
@@ -0,0 +1,152 @@
1
+ # Technical reference
2
+
3
+ Read this when you need field definitions, retrieval limits, storage behavior, or Python examples. For the main workflow, start with the [agent guide](agent-guide.md). CLI commands work after package installation. Commands using repository examples or scripts require a checkout. The Python API keeps its compatible `reverse_google_flights` import name.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ uv sync --extra dev
9
+ ```
10
+
11
+ The browser provider uses an installed Google Chrome when available. Otherwise, install Playwright Chromium with `uv run playwright install chromium`.
12
+
13
+ The optional `fli` comparison provider uses its latest published release:
14
+
15
+ ```sh
16
+ uv sync --extra dev --extra fli
17
+ ```
18
+
19
+ ## Run a batch
20
+
21
+ Start with one search:
22
+
23
+ ```sh
24
+ agentic-flights examples/search-one.json
25
+ ```
26
+
27
+ Then run the 50-search example if you want to exercise batching:
28
+
29
+ ```sh
30
+ agentic-flights examples/searches-50.json
31
+ ```
32
+
33
+ The command stores the full report in the managed cache. Standard output contains an `rgf_...` run ID, counts, coverage, five short previews, and commands for reading the saved results. Use `--output PATH` to keep a separate export; the automatic cleanup never removes explicit exports. Use `--full` to write the complete report to standard output.
34
+
35
+ Input may also come from standard input. Set `provider` to `browser` or `fli`. Keep `max_workers` at 2 for browser searches. The `ranked_by_currency` field never compares prices across currencies.
36
+
37
+ Use `search_mode: discover` to retrieve many candidates with less browser work. The browser reads Google's Best tab and clicks the visible `View more flights` control up to `load_more_clicks` times. `retrieval_limit` defaults to 250 and caps the candidates read from the page. It does not control `max_results`, complete-quote budgets, or shortlist size. Coverage records loaded candidates, DOM duplicates, parser losses, clicks, and why retrieval stopped. Google's Best tab may omit other available flights.
38
+
39
+ Set `max_stops`, `max_price`, and `segment_filters` to filter the Google query before retrieval. Segment filters accept `airlines`, departure and arrival hour bounds, and `max_duration_minutes`. Put return-flight filters in `return_segment_filters`; each additional multi-city segment has its own `filters`. Hours are airport-local integers from 0 through 23, and a window cannot cross midnight. Airlines use two-character IATA codes or the identifiers `ONEWORLD`, `SKYTEAM`, and `STAR_ALLIANCE`.
40
+
41
+ Multi-leg discovery returns first-stage `outbound_choice` records with `ticket_scope: partial_or_unknown`. Their prices are provisional; adding one-way prices or presenting them as complete tickets would be incorrect.
42
+
43
+ Filter and rank a saved report without another Google request:
44
+
45
+ ```sh
46
+ agentic-flights list RUN_ID --filters - --page-size 5 <<'JSON'
47
+ {"max_price": 150, "max_total_stops": 0, "limit": 5}
48
+ JSON
49
+ ```
50
+
51
+ Local filters support request IDs, price, total duration, total stops, airline names or codes, departure hours, overhead cabin baggage, and complete-ticket scope. Missing evidence fails a hard filter and appears in `unknown_by`. The shortlist report preserves source coverage, parse losses, and truncation separately.
52
+
53
+ Inspect a saved report without printing the full JSON:
54
+
55
+ ```sh
56
+ agentic-flights summary RUN_ID
57
+ agentic-flights list RUN_ID --page-size 5
58
+ agentic-flights show RUN_ID RESULT_ID
59
+ ```
60
+
61
+ `list` filters before pagination and returns at most 20 slim records. Its cursor binds the dataset contents, filter, sort order, and offset. `show` accepts at most ten stable result IDs and is the only default view that returns full fare evidence and booking URLs. Use `summary SOURCE --full` for an explicit full export.
62
+
63
+ Managed runs live in the platform's user cache directory, and reading a run refreshes its idle timestamp. Cleanup keeps at most 50 runs, removes runs idle for 7 days, and limits managed reports plus the provider cache to 100 MB. Cleanup touches only regular files inside the store marked as owned by this application. System temporary folders, project artifacts, explicit exports, and explicit `--cache-dir` paths are outside its scope.
64
+
65
+ Each browser result includes numeric price, duration, stop count, airline name, local departure time, and local arrival time. A one-way result normally has `ticket_scope: partial_or_unknown` because the search page does not prove the final ticket terms. A hard cabin-bag requirement uses the same bounded final-page exploration as complete trips.
66
+
67
+ For a round trip, the browser provider explores a bounded tree of outbound and return choices. It replays every selected path in a fresh page and accepts a result only after Google shows a final booking page. Each option contains both journeys, the exact combined price from the itinerary summary, `price_provenance: provider_final_total`, and `ticket_scope: complete_single_ticket`. The provider does not add independent one-way fares or continue to a seller.
68
+
69
+ For an open-jaw or another multi-city ticket, keep the first journey in `origin`, `destination`, and `departure_date`. Add later journeys to `additional_segments` in travel order. The same bounded traversal verifies each returned path on its own final page. The input schema rejects requests that combine `additional_segments` with `return_date`.
70
+
71
+ Use `candidates_per_stage` to control the Google-ordered choices explored at each journey. Use `max_complete_quotes` and `max_browser_transitions` as total work budgets. `max_results` only limits returned options. Defaults are 2 candidates per stage, 4 completed quote attempts, and 24 browser transitions. Complete-ticket options sort by final price, total duration, stop count, and itinerary identity.
72
+
73
+ Every outcome has `coverage`. It reports candidates seen, terminal branches attempted, quotes completed and filtered, duplicates, pruned branches, branch errors by code, browser transitions, and budget exhaustion. `fully_explored` is true only when no candidate, output, error, or budget truncated the observed tree. A successful bounded result is not a guarantee of Google's global lowest fare. Cached outcomes retain the original discovery coverage while `requests_made` is zero for the cache replay.
74
+
75
+ For agent-driven multi-leg work, discover route and date queries first, apply upstream constraints, and shortlist the saved provisional results. Run `search_mode: verify` only for the two or three shortlisted trip queries that need a final seller quote and baggage evidence. Verification reruns current Google results; a provisional rank does not lock the same flight after inventory or prices change.
76
+
77
+ Set `overhead_cabin_bags` to the requested number of overhead cabin bags. With `require_overhead_cabin_bag: true`, the selected fare passes only when its seller option or selected-flight summary states that carry-on baggage is included for the entire trip. Missing evidence, extra-cost baggage, and personal-item-only allowances fail the filter with a structured error.
78
+
79
+ ## Programmatic trip exploration
80
+
81
+ `SearchSpace` expands airport choices, an inclusive departure window, and optional
82
+ stay lengths into exact searches. `space.count` previews the work without opening
83
+ a browser. Airport lists are normalized and deduplicated; same-airport routes are
84
+ excluded. This first API supports one-way and round-trip spaces with at most
85
+ 100,000 combinations. Supply explicit airport codes; city resolution and adaptive
86
+ search allocation are not implemented.
87
+
88
+ ```python
89
+ from datetime import date, timedelta
90
+
91
+ from reverse_google_flights import BatchExecutor, Exploration, SearchSpace, SearchSpec
92
+ from reverse_google_flights.cache import FileCache
93
+ from reverse_google_flights.provider import BrowserProvider
94
+ from reverse_google_flights.store import ManagedStore
95
+
96
+ departure = date.today() + timedelta(days=60)
97
+ space = SearchSpace(
98
+ template=SearchSpec(
99
+ request_id="template", origin="MAD", destination="NRT",
100
+ departure_date=departure, currency="EUR", language="en", country="ES",
101
+ search_mode="discover",
102
+ ),
103
+ origins=["MAD", "BCN"], destinations=["NRT", "HND", "KIX"],
104
+ departure_start=departure, departure_end=departure + timedelta(days=6),
105
+ min_nights=12, max_nights=15,
106
+ )
107
+ assert space.count == 168
108
+ store = ManagedStore()
109
+ store.initialize()
110
+ executor = BatchExecutor(
111
+ FileCache(store.provider_cache / "browser", namespace=BrowserProvider.version),
112
+ max_workers=2,
113
+ )
114
+ exploration = Exploration(space, executor, store)
115
+ progress = exploration.advance(search_budget=20)
116
+ print(progress.model_dump_json()) # Counts, run ID, and at most five previews.
117
+
118
+ # A later process can reconstruct the same configuration and continue.
119
+ progress = exploration.advance(resume_from=progress.run_id, search_budget=20)
120
+ ```
121
+
122
+ Each call attempts at most `search_budget` combinations, including cache hits,
123
+ with a maximum of 500. This is a query budget, not a browser-transition or elapsed
124
+ time limit. Template settings still bound the work inside each query. Full results
125
+ stay in the managed store; use the existing `list` and `show` commands with the
126
+ returned run ID to inspect them without another Google request.
127
+
128
+ Each completed batch produces an immutable snapshot containing all outcomes so
129
+ far. Save the latest run ID to resume. Reusing an older ID branches from that
130
+ snapshot; it does not advance a shared job. An interruption before a batch returns
131
+ can require replaying that batch, with available successful cache entries reused.
132
+ Managed-store expiration still applies, and snapshots retain their original fare
133
+ observations. Changed search-space or provider-namespace settings reject resume.
134
+
135
+ Failures count as attempted and remain in the report. `search_space_exhausted`
136
+ means every defined query was attempted, even if some failed; it does not mean
137
+ every available fare was retrieved. Retry failed queries or verify shortlisted
138
+ queries separately through `BatchExecutor`. MCP transport and automatic finalist
139
+ selection are not part of this API yet.
140
+
141
+ ## Limits
142
+
143
+ Google Flights does not publish a consumer search API. Both providers depend on undocumented behavior that Google can change. Each browser result records `observed_at`; it is a dated observation and does not guarantee the fare. Confirm the price, baggage rules, and availability with the seller before purchase. The browser parser reads English result labels even when the request specifies another language tag.
144
+
145
+ Use conservative batch sizes and concurrency. You are responsible for complying with the terms, policies, and laws that apply to your use. Do not use this project to evade access controls, overload services, or automate purchases.
146
+
147
+ The input schema requires concrete travel dates, so the checked-in examples eventually expire. Update them with:
148
+
149
+ ```sh
150
+ uv run python scripts/refresh_example_dates.py
151
+ ```
152
+
@@ -0,0 +1,52 @@
1
+ {
2
+ "provider": "browser",
3
+ "max_workers": 1,
4
+ "cache_ttl_seconds": 3600,
5
+ "ranking_limit": 8,
6
+ "searches": [
7
+ {
8
+ "request_id": "round-trip-2x2",
9
+ "origin": "MAD",
10
+ "destination": "LIS",
11
+ "departure_date": "2027-01-21",
12
+ "return_date": "2027-01-28",
13
+ "cabin": "economy",
14
+ "max_stops": "non_stop",
15
+ "adults": 1,
16
+ "overhead_cabin_bags": 1,
17
+ "require_overhead_cabin_bag": true,
18
+ "currency": "EUR",
19
+ "language": "en-US",
20
+ "country": "ES",
21
+ "max_results": 4,
22
+ "candidates_per_stage": 2,
23
+ "max_complete_quotes": 4,
24
+ "max_browser_transitions": 24
25
+ },
26
+ {
27
+ "request_id": "open-jaw-2x2",
28
+ "origin": "MAD",
29
+ "destination": "CDG",
30
+ "departure_date": "2026-11-18",
31
+ "additional_segments": [
32
+ {
33
+ "origin": "AMS",
34
+ "destination": "MAD",
35
+ "departure_date": "2026-11-25"
36
+ }
37
+ ],
38
+ "cabin": "economy",
39
+ "max_stops": "one_stop_or_fewer",
40
+ "adults": 1,
41
+ "overhead_cabin_bags": 1,
42
+ "require_overhead_cabin_bag": true,
43
+ "currency": "EUR",
44
+ "language": "en-US",
45
+ "country": "ES",
46
+ "max_results": 4,
47
+ "candidates_per_stage": 2,
48
+ "max_complete_quotes": 4,
49
+ "max_browser_transitions": 24
50
+ }
51
+ ]
52
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "provider": "browser",
3
+ "max_workers": 1,
4
+ "cache_ttl_seconds": 3600,
5
+ "ranking_limit": 5,
6
+ "searches": [
7
+ {
8
+ "request_id": "round-trip-with-cabin-bag",
9
+ "origin": "MAD",
10
+ "destination": "LIS",
11
+ "departure_date": "2027-01-21",
12
+ "return_date": "2027-01-28",
13
+ "cabin": "economy",
14
+ "max_stops": "non_stop",
15
+ "adults": 1,
16
+ "overhead_cabin_bags": 1,
17
+ "require_overhead_cabin_bag": true,
18
+ "currency": "EUR",
19
+ "language": "en-US",
20
+ "country": "ES",
21
+ "max_results": 1
22
+ },
23
+ {
24
+ "request_id": "open-jaw-with-cabin-bag",
25
+ "origin": "MAD",
26
+ "destination": "CDG",
27
+ "departure_date": "2026-11-18",
28
+ "additional_segments": [
29
+ {
30
+ "origin": "AMS",
31
+ "destination": "MAD",
32
+ "departure_date": "2026-11-25"
33
+ }
34
+ ],
35
+ "cabin": "economy",
36
+ "max_stops": "one_stop_or_fewer",
37
+ "adults": 1,
38
+ "overhead_cabin_bags": 1,
39
+ "require_overhead_cabin_bag": true,
40
+ "currency": "EUR",
41
+ "language": "en-US",
42
+ "country": "ES",
43
+ "max_results": 1
44
+ }
45
+ ]
46
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "provider": "browser",
3
+ "max_workers": 1,
4
+ "cache_ttl_seconds": 3600,
5
+ "ranking_limit": 4,
6
+ "searches": [
7
+ {
8
+ "request_id": "open-jaw-two-quotes",
9
+ "origin": "MAD",
10
+ "destination": "CDG",
11
+ "departure_date": "2026-11-18",
12
+ "additional_segments": [
13
+ {
14
+ "origin": "AMS",
15
+ "destination": "MAD",
16
+ "departure_date": "2026-11-25"
17
+ }
18
+ ],
19
+ "cabin": "economy",
20
+ "max_stops": "one_stop_or_fewer",
21
+ "adults": 1,
22
+ "overhead_cabin_bags": 1,
23
+ "require_overhead_cabin_bag": true,
24
+ "currency": "EUR",
25
+ "language": "en-US",
26
+ "country": "ES",
27
+ "max_results": 2,
28
+ "candidates_per_stage": 2,
29
+ "max_complete_quotes": 2,
30
+ "max_browser_transitions": 14
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,63 @@
1
+ {
2
+ "provider": "browser",
3
+ "max_workers": 1,
4
+ "cache_ttl_seconds": 3600,
5
+ "ranking_limit": 10,
6
+ "searches": [
7
+ {
8
+ "request_id": "mad-lhr-broad",
9
+ "origin": "MAD",
10
+ "destination": "LHR",
11
+ "departure_date": "2027-01-21",
12
+ "cabin": "economy",
13
+ "max_stops": "any",
14
+ "adults": 1,
15
+ "currency": "EUR",
16
+ "language": "en-US",
17
+ "country": "ES",
18
+ "search_mode": "discover",
19
+ "retrieval_limit": 250,
20
+ "load_more_clicks": 1
21
+ },
22
+ {
23
+ "request_id": "mad-lhr-upstream-filtered",
24
+ "origin": "MAD",
25
+ "destination": "LHR",
26
+ "departure_date": "2027-01-21",
27
+ "cabin": "economy",
28
+ "max_stops": "non_stop",
29
+ "adults": 1,
30
+ "currency": "EUR",
31
+ "language": "en-US",
32
+ "country": "ES",
33
+ "max_price": 200,
34
+ "segment_filters": {
35
+ "airlines": [
36
+ "IB"
37
+ ],
38
+ "earliest_departure_hour": 7,
39
+ "latest_departure_hour": 18,
40
+ "max_duration_minutes": 180
41
+ },
42
+ "search_mode": "discover",
43
+ "retrieval_limit": 250,
44
+ "load_more_clicks": 1
45
+ },
46
+ {
47
+ "request_id": "mad-lis-round-trip-discovery",
48
+ "origin": "MAD",
49
+ "destination": "LIS",
50
+ "departure_date": "2027-01-21",
51
+ "return_date": "2027-01-28",
52
+ "cabin": "economy",
53
+ "max_stops": "one_stop_or_fewer",
54
+ "adults": 1,
55
+ "currency": "EUR",
56
+ "language": "en-US",
57
+ "country": "ES",
58
+ "search_mode": "discover",
59
+ "retrieval_limit": 100,
60
+ "load_more_clicks": 1
61
+ }
62
+ ]
63
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "provider": "browser",
3
+ "max_workers": 1,
4
+ "cache_ttl_seconds": 3600,
5
+ "ranking_limit": 5,
6
+ "searches": [
7
+ {
8
+ "request_id": "mad-lis-round-trip",
9
+ "origin": "MAD",
10
+ "destination": "LIS",
11
+ "departure_date": "2026-12-04",
12
+ "return_date": "2026-12-12",
13
+ "cabin": "economy",
14
+ "max_stops": "non_stop",
15
+ "adults": 1,
16
+ "currency": "EUR",
17
+ "language": "en-US",
18
+ "country": "ES",
19
+ "max_results": 5
20
+ }
21
+ ]
22
+ }