> **For coding agents and LLMs:** This is one page from the Social Fetch docs (markdown export). For curated orientation and workflow guidance, start with [`/llms.txt`](https://www.socialfetch.dev/llms.txt); for agent onboarding and crawl rules, use [`/agents.txt`](https://www.socialfetch.dev/agents.txt); for the full endpoint list with links to pages like this one, use [`/llms-endpoints.txt`](https://www.socialfetch.dev/llms-endpoints.txt); for one platform's parameters and curls, use [`/llms-{platform}.txt`](https://www.socialfetch.dev/llms-tiktok.txt); use [`/llms.json`](https://www.socialfetch.dev/llms.json) when you need structured JSON for tool registration.

## This page

- **On-site (HTML):** <https://www.socialfetch.dev/docs/recipes/write-a-query-from-a-description>
- **Markdown (.mdx) URL:** <https://www.socialfetch.dev/docs/recipes/write-a-query-from-a-description.mdx>

## API base URL and authentication

- **API origin (from OpenAPI `servers`):** `https://api.socialfetch.dev`
- **Authentication:** send `x-api-key: sfk_...` on `/v1/**` and `/v2/**` routes unless the operation is explicitly anonymous (check OpenAPI `security`, the [API reference hub](https://www.socialfetch.dev/docs/api.mdx), [`/llms.txt`](https://www.socialfetch.dev/llms.txt), or [`/llms.json`](https://www.socialfetch.dev/llms.json) for each route).
- **OpenAPI JSON:** <https://www.socialfetch.dev/openapi.json>

## Recommended docs entrypoints (this site)

- [Documentation overview](https://www.socialfetch.dev/docs.mdx) — top-level orientation (markdown).
- [Quickstart](https://www.socialfetch.dev/docs/quickstart.mdx) — authenticate with `x-api-key`, validate auth with `whoami`, and understand the JSON envelope.
- [SDK](https://www.socialfetch.dev/docs/sdk.mdx) — official TypeScript SDK guide, including `SocialFetchClient`, `Result`, and `unwrap()`.
- [Capability matrix](https://www.socialfetch.dev/docs/capability-matrix.mdx) — every operation with identifiers, pagination, outcomes, media download, credits, and its SDK method. Generated from OpenAPI, so use it for route selection instead of scanning individual pages.
- [Recipes](https://www.socialfetch.dev/docs/recipes.mdx) — copyable workflows (brand monitoring, transcripts, Ad Library, creator scoring, Reddit research) with credit callouts and SDK examples.
- [Integrations](https://www.socialfetch.dev/docs/integrations.mdx) — MCP for AI clients, n8n verified node, Apify Store Actors, Make custom app, SDK, and REST API connection paths.
- [MCP product page](https://www.socialfetch.dev/mcp) — hosted MCP overview, OAuth, Skills install.
- [MCP integration](https://www.socialfetch.dev/docs/integrations/mcp.mdx) — hosted `/mcp` server, OAuth, Cursor/VS Code/Claude install snippets, 910 endpoint tools, plus docs_search/docs_read for implementation help.
- [n8n integration](https://www.socialfetch.dev/docs/integrations/n8n.mdx) — install `n8n-nodes-socialfetch`, credentials, and workflow examples.
- [Apify integration](https://www.socialfetch.dev/docs/integrations/apify.mdx) — Store Actors under @social-fetch, PPE billing, dataset export, and quick start.
- [Make integration](https://www.socialfetch.dev/docs/integrations/make.mdx) — custom app modules for Make scenarios, API key credentials, and module catalog.
- [`/llms-endpoints.txt`](https://www.socialfetch.dev/llms-endpoints.txt) — every documented operation with a direct link to that route's agent-readable markdown page (prefer this over parsing OpenAPI).
- [`/llms-{platform}.txt`](https://www.socialfetch.dev/llms-tiktok.txt) — per-platform endpoint files generated from OpenAPI (parameters, credits, curls).
- [`/agents.txt`](https://www.socialfetch.dev/agents.txt) — agent crawl/onboarding file with capabilities, auth rules, and allowlist.
- [`/llms.json`](https://www.socialfetch.dev/llms.json) — structured machine-readable operation inventory with parameter names, pagination, outcomes, credits, and SDK mapping.
- [API reference hub](https://www.socialfetch.dev/docs/api.mdx) — human-friendly index of operations with links into generated pages.
- [Errors](https://www.socialfetch.dev/docs/errors.mdx) — shared error envelope and HTTP status guidance.
- [Credits](https://www.socialfetch.dev/docs/credits.mdx) — metering, `402`, and planning batch jobs.
- Outcome semantics such as `found`, `not_found`, and `private` are documented in [Errors](https://www.socialfetch.dev/docs/errors.mdx) and on operation pages when present in the OpenAPI contract.

## Markdown docs convention

- Every docs page has a markdown twin: append **`.mdx`** to the docs pathname (for example `/docs/quickstart` → `/docs/quickstart.mdx`).
- Agents that send `Accept: text/markdown` on `/docs/**` HTML URLs may receive markdown directly (same URL, `Vary: Accept`).
- Published blog posts use the same convention: `/blog/{slug}` → `/blog/{slug}.mdx`, or `Accept: text/markdown` on the HTML URL (`Vary: Accept`).

---
# Write a Boolean query from a description (https://www.socialfetch.dev/docs/recipes/write-a-query-from-a-description)

{/* Request and response come from packages/public-api-contract/src/endpoints/query-suggest.examples.ts, the engine's real answer (`checkSuggestion`). Both endpoints are free: x-socialfetch-pricing is 0 for /v1/query/suggest and /v1/query/validate in apps/web/openapi/openapi.json. */}

This recipe turns a plain description of what you want to monitor into a Boolean query in Social Fetch's syntax. AI writes the query; Social Fetch then reads it, explains it in plain words, plans how each platform would search it and runs it over example posts you give it, all before anything is searched. You review it, refine it in plain words or edit it, and use it once it finds what you meant.

## What you'll need

* An [API key](https://app.socialfetch.dev/api-keys). The [Quickstart](/docs/quickstart) shows how to create one.
* Optionally, a few posts the query should find and a few it should leave out.

No code? The free [Boolean Query Translator](/tools/boolean-query-translator) does the same in the browser: choose **Describe it**.

## What it costs

Both routes in this recipe are free: [`POST /v1/query/suggest`](/docs/api/v1/query/suggest/post) and [`POST /v1/query/validate`](/docs/api/v1/query/validate/post) charge 0 credits and search nothing. Writing a query is limited to 10 requests a minute and 300 a day per workspace, and checking one to 60 a minute.

## Step 1: Describe what to monitor

Send what you want in `brief`, in any language. Add `languages` when the posts must be in particular languages, `platforms` to plan for only some, and `examples` with posts it should and shouldn't find.

```typescript
const response = await fetch("https://api.socialfetch.dev/v1/query/suggest", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "x-api-key": process.env.SOCIALFETCH_API_KEY!,
  },
  body: JSON.stringify({
    brief:
      "Complaints about Lumora Foods: recalls, lawsuits and food poisoning, in English and Spanish. Not job ads.",
    examples: {
      shouldMatch: [
        "Lumora Foods is recalling its peanut butter after salmonella was found",
      ],
      shouldNotMatch: ["Lumora Foods is hiring drivers in Leeds"],
    },
    languages: ["en", "es"],
    platforms: ["twitter", "reddit"],
    maxCredits: 30,
  }),
});
const { data } = await response.json();
```

## Step 2: Read what came back

```json
{
  "status": "suggested",
  "query": "(\"Lumora Foods\" OR Lumora OR #LumoraFoods) AND (recall* OR lawsuit* OR demanda* OR \"food poisoning\" OR intoxicación) AND NOT (hiring OR vacante) AND (lang:en OR lang:es)",
  "explanation": "Posts that ((mention “Lumora Foods” or “Lumora”) or use the hashtag #LumoraFoods), (mention a word starting with “recall”, a word starting with “lawsuit”, a word starting with “demanda”, “food poisoning” or “intoxicación”), (don't mention “hiring” and don't mention “vacante”) and (are in English (en) or are in Spanish (es)).",
  "note": "Added Spanish words for lawsuits and food poisoning, and left out job ads.",
  "examples": {
    "shouldMatch": [{ "outcome": "match", "agrees": true, "matchedTerms": ["\"Lumora Foods\"", "Lumora", "recall*", "lang:en"], "unevaluableFields": [] }],
    "shouldNotMatch": [{ "outcome": "no-match", "agrees": true, "matchedTerms": [], "unevaluableFields": [] }]
  },
  "estimatedCredits": 18,
  "warnings": [],
  "aiGenerated": true
}
```

| Field                           | What it tells you                                                                                                                                                      |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query`                         | The query AI wrote. Nothing has searched with it.                                                                                                                      |
| `explanation`                   | What the query finds, written from the query itself, never by the AI, so it always says what the query does. Read it before you use the query.                         |
| `note`                          | A short note from the AI on what it added or assumed. Written by AI.                                                                                                   |
| `examples`                      | How the query reads each example post, in the order you sent them. `unknown` means it depends on something the text doesn't show, such as who wrote it.                |
| `platforms`, `estimatedCredits` | How each platform would search it, what it can't, and the most a search would cost.                                                                                    |
| `warnings`                      | What to check: example posts it gets wrong, names from your description it left out, languages you asked for that it doesn't limit to, platforms that can't search it. |
| `aiGenerated`                   | Always `true`. Show the query as written by AI wherever it appears.                                                                                                    |

## Step 3: Refine it in plain words

To change the query, send it back as `previousQuery` with what to change in `brief`. The writer is asked to change only that and keep the rest as it was; compare the new query with the old before you use it.

```typescript
body: JSON.stringify({
  brief: "also leave out posts about recipes",
  previousQuery: data.query,
  examples: { /* the same example posts */ },
}),
```

## Step 4: Check your own edits

After you edit a query by hand, check it with [`POST /v1/query/validate`](/docs/api/v1/query/validate/post). It returns the same explanation, plan and estimate, or where the query can't be read.

## From an AI agent

Connected through [MCP](/docs/integrations/mcp), the same thing is the `query_suggest_post` tool. Show the user the query, its explanation and its warnings, and let them accept or change it before searching with it.

## When no query is written

| Response                                                        | Means                                                 | Do                                                                           |
| --------------------------------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------- |
| `200` with `status: "refused"`, `refusal.reason: "not_a_brief"` | The description names no posts to monitor             | Say what the posts should be about: a brand, people, topics, places or words |
| `200` with `refusal.reason: "no_valid_query"`                   | No query that reads correctly could be written for it | Reword it, or write the query yourself and check it with validate            |
| `200` with `refusal.reason: "unusable_answer"`                  | The answer couldn't be used                           | Reword the description                                                       |
| `429`                                                           | More than 10 requests a minute, or 300 a day          | Retry after `Retry-After` seconds                                            |
| `503`                                                           | Writing a query is unavailable right now              | Retry after `Retry-After` seconds                                            |

## What's sent and kept

Your description, example posts and any `previousQuery` are sent to an AI model to write the query, after email addresses, phone numbers, card numbers and keys are removed. For a zero-retention workspace they only go to model endpoints that keep no prompts. Social Fetch logs how long they were, never what they said.