---
title: "Suggest keywords for an intent"
method: POST
path: "/v1/agents/suggest-keywords"
tags: ["Content Research Agents"]
---

# Suggest keywords for an intent

`POST /v1/agents/suggest-keywords`

## Request

- Base URL: `https://api.virlo.ai`
- URL: `https://api.virlo.ai/v1/agents/suggest-keywords`
- Auth: HTTP bearer

Given an `intent`, returns a quality-graded keyword set you can pass straight to POST /v1/agents. Free and synchronous — no agent is created and no credits are spent, so you can iterate until the quality score looks right.

## Request body

- V1SuggestKeywordsDto
  - `intent` string, required: What the agent should research, in plain language. Same field you will pass to POST /v1/agents.
  - `topic_hint` string: Short topic or agent-name hint to steer generation.
  - `platforms` string[]: Platforms the agent will collect from.
  - `existing_keywords` string[]: The agent’s current keywords. Required in practice for `refresh` and `opportunity` modes so the engine can avoid repeating them.
  - `mode` 'create' | 'refresh' | 'opportunity': `create` (default) builds a fresh set from the intent. `refresh` replaces stale keywords on an existing agent. `opportunity` looks for under-covered angles adjacent to the current set.
  - `desired_count` number: How many keywords to aim for. Clamped by the engine to the data-backed 7-12 sweet spot — beyond ~15 keywords the off-target ratio climbs sharply.
  - `use_web_grounding` boolean: Ground generation in current web results so timely phrasing is picked up. Slower; leave off for evergreen niches.

## Response `200`

Suggested keywords

- V1SuggestKeywordsResponseDto
  - `data` SuggestKeywordsDataDto, required
    - `keywords` string[], required
    - `exclude_keywords` string[], required: Ready-to-use noise terms — pass this array straight into an agent as exclude_keywords.
    - `reasoning` string, nullable, required
    - `quality` SuggestKeywordsQualityDto, required
      - `score` number, nullable, required: 0-100.
      - `passes` boolean, nullable, required: True when the score clears the threshold and there are no critical issues.
      - `issues` SuggestKeywordsQualityIssueDto[], required
        - `code` 'too_few_keywords' | 'too_many_keywords' | 'single_word_keywords' | 'overly_long_keywords' | 'duplicate_keywords' | 'low_cluster_cohesion' | 'empty_set', required
        - `severity` 'critical' | 'warning' | 'info', required
        - `message` string, required
        - `offenders` string[]: The keywords that triggered this issue, when applicable.
      - `stats` SuggestKeywordsQualityStatsDto, required
        - `count` number, nullable, required
        - `avg_words_per_keyword` number, nullable, required
        - `single_word_count` number, nullable, required
        - `long_keyword_count` number, nullable, required
        - `duplicate_count` number, nullable, required
        - `core_token_coverage` number, nullable, required: Fraction (0-1) of keywords sharing the dominant core token in `core_token`.
        - `core_token` string, nullable, required
    - `timely_context_used` boolean, required: Whether web grounding contributed timely context.

## Other responses

- `400`: Invalid request parameters
- `401`: Unauthorized
- `429`: Rate limit exceeded (`rate_limit_exceeded`); see the Retry-After header

---

[API](https://skmtc.dev/virlo/apis/virlo-public-api.md) · [All operations](https://skmtc.dev/virlo/apis/virlo-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/virlo/apis/virlo-public-api/revisions/3b69c6145087?raw)
