---
title: "Generate startup ideas"
method: POST
path: "/api/agent/tools/idea-generator"
tags: ["Tools"]
---

# Generate startup ideas

`POST /api/agent/tools/idea-generator`

## Request

- Base URL: `https://preuve.ai`
- URL: `https://preuve.ai/api/agent/tools/idea-generator`
- Auth: API key in header `x-preuve-key`

Synchronous idea generation - the backing route for the generate_ideas MCP tool. Spends no scan quota or tokens. Free accounts get 5 generations per rolling 24 hours per account (GENERATION_LIMIT when exhausted). Paid-or-credited accounts skip that cap and get limit: null, under a daily fair-use ceiling of 20 generations per rolling 24 hours per account, 15 of them with the Pro model (a Pro run counts toward both), shared with the idea generator on preuve.ai and refused with DAILY_LIMIT_REACHED. The Pro model is the default on paid plans (pro: false opts out) and the response pro says whether the Pro tier was used (under load a Pro-tier run can be served by a faster fallback model); pro: true without a paid plan is refused with PRO_PAID_REQUIRED, and a pro that is not a boolean or null is 400 INVALID_INPUT. Founder-profile tailoring is automatic on paid plans with a saved profile (fit: false opts out); the response's fitReason and fitHint state why tailoring did or did not apply.

## Request body

- object
  - `interests` string, required: Interests or skills to generate ideas from.
  - `budget` string
  - `audience` string
  - `industry` string
  - `language` string
  - `pro` boolean, nullable: Use the Pro model. Defaults to true on paid plans and false otherwise (null counts as omitted); true without a paid plan is refused.
  - `fit` boolean: Founder-profile tailoring is automatic on paid plans with a saved profile; pass false to opt out. Passing true is never needed.

## Response `200`

Generated ideas. remaining and limit are null for paid-or-credited accounts, whose daily ceiling is not counted down here; free accounts see the rolling-24h window state. Pain points are teased as locked on non-paid plans.

- object
  - `ideas` GeneratedIdea[]
    - `name` string, required
    - `pitch` string, required
    - `whyNow` string, required: The dated shift that makes the idea timely.
    - `saturation` 'LOW' | 'MEDIUM' | 'HIGH', required
    - `audience` 'B2B' | 'B2C' | 'Both', required
    - `tags` string[], required
    - `sources` string[], required
    - `painPoint` string, nullable: A real, sourced complaint behind the idea. Null when search found none, and null with painPointLocked=true on plans that do not include pain points.
    - `painPointLocked` boolean: Present only when a pain point exists but the plan hides it.
  - `remaining` integer, nullable
  - `limit` integer, nullable
  - `pro` boolean
  - `fit` boolean
  - `fitReason` 'applied' | 'disabled' | 'requires_paid_plan' | 'no_profile'
  - `fitHint` string: Present when tailoring did not apply for a reason worth relaying to the user (no_profile, requires_paid_plan).

## Other responses

- `400`: The request was rejected before any work started: malformed JSON (INVALID_JSON), an id that is not one of ours (INVALID_ANALYSIS_ID, INVALID_BATCH_ID), an export format we do not serve (UNSUPPORTED_FORMAT), or a field the schema cannot express - an idea too short to analyse, a batch outside 1..5 items (INVALID_BATCH_SIZE), two batch items sharing a clientRunId (DUPLICATE_CLIENT_RUN_ID), an unknown scanType. No paid quota, tokens, or scans were consumed. On the CREATE routes a rate-limit window can still be: those per-account limiters are claimed ahead of schema validation on purpose, so that malformed spam is metered, and retrying unchanged spends another slot. Enrich no longer works that way - since 2026-09-15 its 12/min window is claimed after the body is validated, so a malformed enrich costs nothing. Fix the request either way; retrying it unchanged returns the same answer.
- `401`: Missing x-preuve-key header (MISSING_API_KEY), or a malformed, unknown or revoked key (INVALID_AGENT_KEY - the same envelope for all three, on purpose). Repeated failures are throttled per IP and answer 429.
- `403`: Pro model requested without a paid plan (PRO_PAID_REQUIRED, body carries an upsell object), or the key lacks analysis:write (INSUFFICIENT_SCOPE - see the InsufficientScope response).
- `413`: The request body is over the 1 MB limit (PAYLOAD_TOO_LARGE, with the ceiling in `limit`). Counted while the body streams in and refused the moment it is exceeded, before any quota, token, or scan is consumed. Not retryable unchanged: send less, or split the work across requests.
- `429`: Free generation cap reached (GENERATION_LIMIT, body carries the limit and an upsell object, no Retry-After), the paid daily ceiling reached (DAILY_LIMIT_REACHED, body carries the limit that refused, 20 or 15 for a Pro run, with no Retry-After and no resetAt), or the hourly abuse limit (RATE_LIMITED, with Retry-After).
- `500`: Unexpected server error (INTERNAL_ERROR, or TRIGGER_DISPATCH_FAILED when a deep scan could not be handed to the job runner). Never tell the user their quota is safe: on a deep scan the spend can already have happened when the failure hit, and an ambiguous dispatch deliberately keeps the report and its funding because the job may have been accepted before its response was lost. Treat consumption as unknown and retry with backoff. On an operation that takes a clientRunId, retry with the SAME one - that reconciles the original run, whatever state it reached, instead of starting and charging for a second one. On every other operation there is nothing to reconcile: the read or export simply failed, so a plain backed-off retry is the whole recovery.
- `503`: A dependency was unavailable or deliberately paused (SERVICE_DISABLED, LLM_PAUSED, PROFILE_LOOKUP_FAILED, and the fail-closed answer when the rate-limit backend is down). Any authenticated route can answer this one: authentication meters itself through the same fail-closed limiter, so an outage there refuses the request before the handler runs. Retryable, and no paid quota, tokens, or scans were consumed - a pause that lands mid-run refunds the claim it had already made. Back off and try again rather than falling back to a different endpoint.

---

[API](https://skmtc.dev/preuve/apis/preuve-agent-api.md) · [All operations](https://skmtc.dev/preuve/apis/preuve-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/preuve/apis/preuve-agent-api/revisions/b8e99c2a4fc7?raw)
