Skip to content

The candidateCount Parameter in the Gemini API

7 min read · updated August 11, 2026

candidateCount asks one Gemini call to return several independent completions of the same prompt. The prompt is processed once; each completion is generated separately, and that split is exactly what decides the bill.

What the parameter does

candidateCount lives in generationConfig and is documented in Google’s generateContent API reference as the number of generated responses to return, defaulting to 1. The response’s candidates array then contains that many entries, each a complete Content with its own finishReason, its own index and its own safetyRatings.

The important structural point: the candidates are siblings, not alternatives ranked by the model. Nothing in the response says which one is better. They are independent samples drawn from the same distribution with the same temperature and topP you set, differing only because sampling is stochastic. At temperature: 0 you should expect them to be near-identical, which makes asking for several of them a waste.

Support for values above 1 has varied by model generation. Older Gemini models accepted only candidateCount: 1 and rejected anything higher. Check the reference for the model id you call before building on it; a request that is silently capped is worse than one that errors.

The request and what comes back

curl "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent" \
  -H "x-goog-api-key: $GEMINI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{"role": "user", "parts": [{
      "text": "Write a one-line commit message for: fixed off-by-one in the retry backoff."
    }]}],
    "generationConfig": {
      "candidateCount": 3,
      "temperature": 1.0,
      "maxOutputTokens": 32
    }
  }'

The documented response shape, trimmed to the fields that matter:

{
  "candidates": [
    { "content": {"role": "model", "parts": [{"text": "fix: correct off-by-one in retry backoff"}]},
      "finishReason": "STOP", "index": 0 },
    { "content": {"role": "model", "parts": [{"text": "fix(retry): off-by-one in backoff calculation"}]},
      "finishReason": "STOP", "index": 1 },
    { "content": {"role": "model", "parts": [{"text": "fix: retry backoff was off by one"}]},
      "finishReason": "STOP", "index": 2 }
  ],
  "usageMetadata": {
    "promptTokenCount": 24,
    "candidatesTokenCount": 33,
    "totalTokenCount": 57
  }
}

Each candidate carries its own finishReason, and they can differ. One candidate may return STOP while another returns MAX_TOKENS or is cut for safety — so code that reads candidates[0].finishReason and applies it to the batch is wrong. See the full list of finishReason values for what each one obliges you to do.

What it costs

This follows from the token accounting rather than from any special pricing rule. The response reports:

  • promptTokenCount — the input, counted once. The prompt is processed a single time and the KV cache is shared across the branches, which is the entire reason this parameter exists rather than you issuing N requests.
  • candidatesTokenCount — the output, summed across every candidate. Three 11-token answers report 33.
  • totalTokenCount — the two added together.

So the cost model is: input once, output N times. For a short prompt and long answers, three candidates cost close to three times one. For a 100,000-token prompt and a 200-token answer, three candidates cost barely more than one — and that asymmetry is the case where the parameter earns its place.

Note also that maxOutputTokens is a per-candidate cap, not a budget shared across them. Setting maxOutputTokens: 500 with candidateCount: 3 permits up to 1,500 output tokens on the call. If you are sizing a spend cap, multiply.

When you get back fewer than you asked for

The candidates array is not guaranteed to have the length you requested, and this is the failure that reaches production. Three distinct things can shorten it, and only one of them raises an error.

  • The model does not support the value. Where a model rejects candidateCount above 1 you get an HTTP 400 with INVALID_ARGUMENT and the request does not run. That is the good case, because it is loud. The bad case is a surface that accepts the field and serves one candidate anyway — you pay for one, receive one, and your self-consistency logic silently degrades to a single sample.
  • A candidate was filtered. Safety filtering is applied per candidate. Ask for three and one may come back with finishReason: SAFETY and no parts, or be absent entirely. Your selection logic has to cope with two usable answers out of three, and with zero.
  • Nothing came back at all. If the prompt itself was blocked, candidates is empty regardless of what you asked for, and the reason is on promptFeedback rather than on any candidate.

So the defensive shape is: never index candidates[0], filter to the candidates that actually carry text, and decide explicitly what to do when that list is shorter than expected or empty. Taking a majority vote over an array you assumed had three elements and that has one is a bug that produces plausible output, which is the worst kind.

The obvious alternative — issue N separate requests instead — is worth understanding rather than dismissing. It costs more, because the prompt is prefilled N times instead of once, and for a long prompt that difference is most of the bill. What it buys is isolation: each request retries independently, fails independently, and can carry a different temperature or even a different model. If your reason for wanting several answers is robustness against a failed call rather than sampling variety, separate requests are the correct tool and candidateCount is not.

Candidates are draws, not a ranking

Because the candidates are independent samples, their usefulness is entirely a function of your sampling parameters. At high temperature they diverge and give you genuine variety; near zero they converge and you have paid three times for one answer.

What you do with the variety is the part the API does not help with. There is no built-in scoring. The patterns that work are the ones you implement outside the call: pick by a deterministic rule (shortest valid JSON, the one that parses), pick by majority (self-consistency over a task with a checkable answer), or hand all three to a second, cheaper call to choose. If none of those apply, you do not need multiple candidates.

When it is the right tool

  • Self-consistency on a task with a verifiable answer. Arithmetic, extraction, classification — anything where you can compare candidates mechanically and take the mode.
  • A long shared prompt with short outputs. Retrieval over a large document, where the prompt dominates the bill and extra branches are cheap.
  • Offering a human a choice. Three subject lines, three commit messages, three summaries — where the selection step is a person.
  • Not for reliability. Three samples from a model that is wrong in a consistent direction are three wrong answers. Sampling variance is not the same thing as error, and averaging over it does not fix a systematic mistake.

Note that candidateCount does not apply to streaming in a useful way: with streamGenerateContent the chunks carry a candidate index and you would have to demultiplex them yourself. If you are streaming to a user, generate one candidate.