# Validate a query

> Check a Lucene query against query-core before saving it as a rule — validity, diagnostics, and the compiled query, with a public unauthenticated variant.

Check a query without creating a rule. It requires a [tap token](/get-started/authentication)
(`fh_`); there is also an unauthenticated [public variant](#public-variant). Base URL:
`https://api.firehose.com`.

The query is expanded (`$filter` references resolved against your tap's organization) and run
through **query-core** — the same engine the matcher uses — so a `valid` result here is exactly
what will match. Use it to build a query interactively before saving it as a rule.

## Request

```text
POST /v1/validate
```

```json
{ "query": "tesla AND language:en", "quality": true, "nsfw": false }
```

- `query` (string, required) — Lucene query; may contain `$filter` references. See [Rules & query syntax](/stream/rules) for the language.
- `quality` (boolean, default `true`) — apply quality filters; `false` drops the quality URL/age filters from the compiled query.
- `nsfw` (boolean, default `false`) — `true` keeps adult pages instead of excluding them.

Set `quality` and `nsfw` to match the rule you intend to create — they change both the compiled
query and whether the query is matchable.

## Response

A valid query returns `200` with the compiled form:

```json
{
    "core_version": "0.4.0",
    "valid": true,
    "diagnostics": [],
    "compiled": {
        "lucene": "+added:tesla +language:en",
        "effective_lucene": "+(+added:tesla +language:en) #age_seconds:[0 TO 604800] -url:*/category/* ..."
    },
    "info": { "fields_used": ["added", "language"], "default_field_terms": ["tesla"] }
}
```

An invalid query returns `valid: false` and `compiled: null`. Each entry in `diagnostics` carries a
stable `code`, a human `message`, an optional `span` (`{ start, end }`, offsets into the *expanded*
query), and an optional `hint` (`{ message, replacement }`). Only `severity: "error"` diagnostics
make a query invalid — `warning`s, such as an unknown enum value, are advisory.

```json
{
    "core_version": "0.4.0",
    "valid": false,
    "diagnostics": [
        {
            "severity": "warning",
            "code": "unknown_enum_value",
            "message": "'eng' is not a known language value — it will never match",
            "field": "language",
            "span": { "start": 0, "end": 12 },
            "hint": { "message": "Did you mean 'en'?", "replacement": "language:en" }
        },
        {
            "severity": "error",
            "code": "unmatchable_query",
            "message": "This query can never match any document: one of its required terms can never match.",
            "field": null, "span": null, "hint": null
        }
    ],
    "compiled": null,
    "info": { "fields_used": ["language"], "default_field_terms": [] }
}
```

The endpoint returns `422` if `query` is missing or references an unknown `$filter`, and `503` if
query-core is temporarily unavailable (retry). See [Errors & limits](/api-reference/errors-and-limits).

## Public variant

```text
POST /v1/validate/public
```

```json
{ "query": "tesla AND language:en" }
```

Same request and response contract as `POST /v1/validate`, but **requires no authentication** — for
a logged-out try-it page. It validates the query exactly as written: with no organization to resolve
against, `$filter` expansion is not performed, and a query containing `$filter` references is rejected
with `422`. Rate-limited per IP.
