Skip to main content

Public Query API

Any query you publish in Kvery becomes a callable HTTP endpoint. Your application, a script, or another service can hit that endpoint and receive the query's result as JSON — no database credentials on the client side, just a URL.

The endpoint​

Each shared query is addressed by its hash:

https://app.kvery.io/query/api/{hash}

The {hash} is the share identifier generated when you share the query. Treat it like a secret: anyone with the URL (and password, if you set one) can call the endpoint.

The endpoint runs the published version of the query: an unpublished draft does not change what callers get until you publish it. Only an editor of the query who is signed in to Kvery can request the draft, by adding ?draft to the URL.

Methods map to your SQL​

Kvery decides what a request does based on the HTTP method, and a query only answers the methods that match the kind of SQL it contains:

MethodServes queries that…Typical use
GETread (SELECT, EXEC, WITH … SELECT)fetch data
POSTwrite (INSERT, UPDATE, DELETE, REPLACE, ALTER, CREATE)create / submit
PUTwritereplace
PATCHwritepartial update
DELETEwriteremove

So a SELECT query is reachable with GET, while an INSERT/UPDATE/DELETE query is reachable with POST/PUT/PATCH/DELETE.

Passing parameters​

Form variables in your SQL (for example $status) become request inputs. Supply them as query-string parameters on GET or in the request body on write methods — the parameter name matches the variable name without the $ (so $status is passed as status=…):

# GET with a parameter
curl "https://app.kvery.io/query/api/{hash}?status=open"
# POST a write query
curl -X POST "https://app.kvery.io/query/api/{hash}" \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]"}'

Caching​

GET responses that anyone may get — the query is shared without a password, its group has no IP whitelist, the request carries no Authorization header and doesn't come from a signed-in Kvery user — are served with Cache-Control: private, max-age=0: neither the caller nor proxies and CDNs reuse the response, so every request gets the current result. Every other response (Basic Auth, a bearer token or any other Authorization header, a signed-in user, share password, IP whitelist) is marked private, no-store. Queries can also enable Kvery's own result cache, which short-circuits identical reads for speed; cached results are only returned to callers who pass the same checks. Write requests are never cached. See Export & cache for how caching behaves.

Securing the endpoint​

  • Set a password on the share so callers must authenticate. Wrong passwords are counted per address and per link: once there are too many, every attempt from that address for that link is refused, even with the right password, until a while has passed since the last wrong attempt.
  • Set an expiry so the hash stops working after a date.
  • Restrict the group with an IP whitelist.
  • Apply rate limits so the endpoint can't be hammered.

Calling a query that is not shared​

The endpoint of a query that is not shared only answers calls with Basic Auth: the e-mail address and password of a Kvery user who can access the query. Basic Auth sends the password with every request and does not ask for the two-factor code, so for scripts and integrations prefer MCP tokens and the MCP server's REST API. Repeated failed Basic Auth attempts are blocked for a while: the endpoint then answers 429 Too Many Requests with a Retry-After header until 15 minutes have passed since the last failed attempt.

Shaping the response​

Use directives to control status codes and error messages (SET HTTPCODE, ON ERROR RETURN), and column modifiers to shape the JSON (for example ::hidden to drop internal columns, ::json to embed parsed JSON).

Next steps​

  • Send many operations in one call with batch requests.
  • Generate interactive docs for a whole group with Swagger.