Keenable · Web Query Language

Turn the internet into data at runtime.

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.

Get your API key See a dataset it built

One command to connect it to Claude Code, or any MCP client.

0
databases required
1
query builds the table
1,000+
pages it can read at once
7
operators over DuckDB SQL

Agents reason better over data than over text

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

OperatorLaunchesSSOFail %
SpaceX263470.4
CASC108191.9
Rocket Lab28110
CAS Space1070
Galactic Energy8625.0
Arianespace1060

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.

But this table doesn't exist

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.

SourceDate, pad, vehicle, outcomePad coordinates Lift capacityFull history
Quarterly launch listsyesnono split across 7 pages
List of rocket launch sitesnoyes, 127 sites nono launches
Vehicle comparison tablesnono yesno launches
Per-year "in spaceflight" pagespartlyno noby-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.

How it works

  1. Start with an objective

    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?

  2. Discover the task-specific schema

    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.

    launch cadencesuccess rate lift capacity to LEOSSO flights flown pads in usevehicle in service most recent flight

    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:

    country of the padoperating nation land or sea platformfirst launch year crewed capabilitybeyond-Earth missions pad coordinates

    There is no universal launch schema. The objective determines the schema.

  3. Materialize the schema from the live web

    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 row your agent needs date 2026-04-01 pad Kennedy LC-39B vehicle SLS Block 1 destination Lunar free-return lift to LEO 95,000 kg pad coordinates 28.608, −80.604 source 1 — seven quarterly launch lists WEB_FETCH + SEM_EXTRACT_ALL gives date, pad, vehicle, operator, outcome, orbit, payload source 2 — capacity tables WEB_SEARCH + SEM_EXTRACT_ALL gives lift capacity per vehicle source 3 — launch site list WEB_FETCH + SEM_EXTRACT_ALL gives coordinates for 127 sites SEM_NORM joins them: pad names become spaceports, vehicle names become families, and the row closes. Repeat for 530 launches across 28 spaceports. Four queries. No table existed before the first one ran. Ask a different question tomorrow and a different set of columns gets materialized instead.

    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.

  4. Compute over it

    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
    OperatorLaunchesSSO flights FailuresFailure rate
    SpaceX2634710.4%
    CASC1081921.9%
    Rocket Lab281100%
    CAS Space10700%
    Galactic Energy86225.0%
    Arianespace10600%
    China Rocket8400%
    ExPace63116.7%
    LandSpace63116.7%
    Roscosmos14300%
    ISRO52120.0%
    Orienspace2200%

    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.

Search finds pages. WebQL creates data.

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.

Get started

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.

Get your API key See a dataset it built

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

Keenable · made with SELECT* · Ask your own questionShare: XLinkedInReddit