Keenable · Web Query Language
The data your agent needs usually doesn't exist in a database. It doesn't exist on a single page, either. It is scattered across websites, benchmarks, registries, announcements, repositories, reviews and dozens of other sources.
Web Query Language creates the dataset your task needs — at runtime.
One command to connect it to Claude Code, or any MCP client.
Give an agent 1,500 pages about rocket launches and ask a real procurement question: which launch providers can we rely on for a 300 kg satellite to sun-synchronous orbit? Then give it a table instead.
Over text
"…the Electron rocket lifted off from Launch Complex 1 at 04:15 UTC carrying…"
"…Galactic Energy's Ceres-1 failed to reach orbit after an anomaly in the…"
"…marking the company's fourth flight to sun-synchronous orbit this year, following…"
"…the payload was deployed into a 500 km SSO, according to a statement released…"
"…a second-stage issue was reported, though the operator has not confirmed…"
"…CAS Space said the Kinetica 1 vehicle can deliver 2,000 kg to low Earth orbit…"
… and 1,494 more pages
read · extract · remember · compare · reconstruct — then repeat, for every page
Over data
| Operator | Launches | SSO | Fail % |
|---|---|---|---|
| SpaceX | 263 | 47 | 0.4 |
| CASC | 108 | 19 | 1.9 |
| Rocket Lab | 28 | 11 | 0 |
| CAS Space | 10 | 7 | 0 |
| Galactic Energy | 8 | 6 | 25.0 |
| Arianespace | 10 | 6 | 0 |
SELECT · FILTER · SORT · GROUP BY · AGGREGATE · JOIN · RANK
Sorting 10,000 rows by a column is a computation. Finding the same ordering by reasoning repeatedly over thousands of pieces of text needs attention, context and interpretation, over and over, with a chance of drift at every step.
With text, agents reconstruct data before they can reason. With data, they compute directly.
That is the problem. There is no registry that holds every launch together with its pad, its pad's coordinates, its vehicle's lift capacity, its destination and its outcome. We went looking. Here is what each source actually has.
| Source | Date, pad, vehicle, outcome | Pad coordinates | Lift capacity | Full history |
|---|---|---|---|---|
| Quarterly launch lists | yes | no | no | split across 7 pages |
| List of rocket launch sites | no | yes, 127 sites | no | no launches |
| Vehicle comparison tables | no | no | yes | no launches |
| Per-year "in spaceflight" pages | partly | no | no | by-spaceport tables only 2024–2026 |
Checked directly: the per-year pages truncate before their own statistics sections, so per-site history earlier than 2024 is not published in a usable form anywhere.
And even if somebody assembled that table, the next question would need different columns. Ask about geopolitics instead of procurement and every attribute changes.
The dataset depends on the objective. WebQL creates it when you ask.
Not a schema, and not a database. An objective:
Which launch providers can we rely on for a 300 kg satellite to sun-synchronous orbit?
Before collecting thousands of values, the agent has to work out what actually matters. So it explores the domain first — how providers are compared, what a payload class means, what counts as reliability, which orbits a vehicle can reach — and builds an ontology for this task.
Change the objective and the schema changes with it. Ask instead who controls access to orbit, and from whose soil? and none of those columns survive:
There is no universal launch schema. The objective determines the schema.
Now WebQL treats that schema as a query over the internet. Each column is resolved from wherever the answer actually lives, and the cells are joined into one row at runtime. Below is a single real row from the launch dataset, with every cell traced to where it came from.
One row, three origins — no single source holds it
The extraction is the part that has no equivalent in a search API.
SEM_EXTRACT reads a page and returns a named field; SEM_MATCH keeps
only the rows that really state the thing; SEM_NORM makes "Land Space" and
"LandSpace" the same key so a count is honest.
SELECT url, UNNEST(
SEM_EXTRACT_ALL(
content,
'a single rocket launch listed in the launch table',
launch_date := 'date of the launch, YYYY-MM-DD',
site := 'launch site or pad it lifted off from',
rocket := 'launch vehicle name',
operator := 'organisation that performed the launch',
outcome := 'whether the launch was a success or a failure',
orbit := 'the orbit or destination reached, as the table states it'
),
recursive := true
)
FROM WEB_FETCH(
'https://en.wikipedia.org/wiki/List_of_spaceflight_launches_in_January–March_2026',
'https://en.wikipedia.org/wiki/List_of_spaceflight_launches_in_April–June_2026'
-- …and the five other quarters
)
There was no table before the query. The query creates the table.
Once the web is structured, the agent has a working dataset instead of a pile of search results — and the objective becomes an ordinary aggregation.
SELECT SEM_NORM(operator, 'merge spelling variants of the same operator') AS operator,
COUNT(*) AS launches,
COUNT(CASE WHEN orbit ILIKE '%SSO%' THEN 1 END) AS sso,
ROUND(100.0 * COUNT(CASE WHEN lower(outcome) LIKE '%fail%' THEN 1 END)
/ COUNT(*), 1) AS failure_pct
FROM r7eb4b1089e7 -- the table the previous query materialized
WHERE lower(orbit) NOT LIKE '%suborbital%'
GROUP BY SEM_NORM(operator, 'merge spelling variants of the same operator')
HAVING COUNT(CASE WHEN orbit ILIKE '%SSO%' THEN 1 END) >= 2
ORDER BY sso DESC
| Operator | Launches | SSO flights | Failures | Failure rate |
|---|---|---|---|---|
| SpaceX | 263 | 47 | 1 | 0.4% |
| CASC | 108 | 19 | 2 | 1.9% |
| Rocket Lab | 28 | 11 | 0 | 0% |
| CAS Space | 10 | 7 | 0 | 0% |
| Galactic Energy | 8 | 6 | 2 | 25.0% |
| Arianespace | 10 | 6 | 0 | 0% |
| China Rocket | 8 | 4 | 0 | 0% |
| ExPace | 6 | 3 | 1 | 16.7% |
| LandSpace | 6 | 3 | 1 | 16.7% |
| Roscosmos | 14 | 3 | 0 | 0% |
| ISRO | 5 | 2 | 1 | 20.0% |
| Orienspace | 2 | 2 | 0 | 0% |
The real output of that query, over 530 launches from January 2025 to 19 August 2026. Rocket Lab and CAS Space carry SSO cadence with no failures; the cheapest small-lift options carry a 17–25% failure rate.
And it does not stop there. The agent can ask another question, add a column, change the population, drill into an outlier, recompute the ranking — against the same materialized set, with no new searching.
On the first pass this table had both "LandSpace" and "Land Space" as
separate rows, splitting one company's record in half. Adding SEM_NORM merged
them and changed the answer. That is what refining a dataset looks like, rather than
re-reading pages.
Materialize once, publish the result as a link, and the same rows become a page a colleague can open — like this map of all 530 launches, built from exactly this dataset.
A search engine answers
Where might this information be?
You get ranked links. Every fact still has to be read out of a page, by something that charges you per token to do it.
A database answers
What does my existing data say?
Fast and exact, over a schema somebody fixed in advance — which means over the questions somebody already anticipated.
Web Query Language answers
What dataset do I need — and what does the internet say when structured that way?
No predefined database. No fixed schema. No assumption that the answer already exists somewhere as a table.
It is one MCP server with one query tool. Connect it and ask in the agent you already use — you do not write the SQL by hand.
claude mcp add --transport http keenable-webql \
https://webql.keenable.ai/mcp \
--header "X-Api-Key: YOUR_KEY"
claude mcp list -- confirm it is connected
A malformed query is rejected by DuckDB before any page is fetched or any model is called, so a bad first draft costs nothing. A real one takes seconds to minutes, because it is reading the live web — spend that on breadth, not on a single page you could have fetched.
Discover the schema.
Materialize it from the web.
Compute over it.
Every number on this page comes from the launch dataset WebQL materialized on 19 August 2026: 530 launches from 28 spaceports, January 2025 to 19 August 2026, assembled by four queries over seven Wikipedia quarterly launch lists, the List of rocket launch sites coordinates column, and published vehicle capacity tables. The operator table is the verbatim output of the query shown above it. Counts are launch attempts, not satellites; SSO flights are those the source records as sun-synchronous. The source-coverage table was checked by fetching each source directly.
Keenable