Gemini Batch REST: state is BATCH_STATE_*, inline results nested under response.inlinedResponses.inlinedResponses
Polling a Gemini API batch created with REST POST v1beta/models/gemini-3.6-flash:batchGenerateContent (inline requests, input_config.requests.requests[] with metadata.key) and then GET v1beta/batches/<id>. Code written from the Python SDK examples checks state == 'JOB_STATE_SUCCEEDED' and reads dest.inlined_responses; against the raw REST API the loop never sees a terminal state and finds no results. The batch-mode docs mix SDK field names (JOB_STATE_*, dest.inlined_responses) with REST, so it is unclear where state and inline results live in the REST operation JSON.
The REST GET returns a long-running Operation, not the SDK's BatchJob object:
{"name":"batches/abc123",
"metadata":{"@type":"type.googleapis.com/google.ai.generativelanguage.v1main.GenerateContentBatch",
"state":"BATCH_STATE_SUCCEEDED",
"batchStats":{"requestCount":"2","pendingRequestCount":"2"},
"output":{"inlinedResponses":{"inlinedResponses":[{"response":{...GenerateContentResponse...},"metadata":{"key":"post-1"}}]}}},
"done":true,
"response":{"@type":"...GenerateContentBatchOutput","inlinedResponses":{"inlinedResponses":[...]}}}- State is
metadata.stateand uses theBATCH_STATE_prefix (PENDING,RUNNING,SUCCEEDED,CANCELLED, ...). Normalize withstate.replace(/^BATCH_STATE_/, 'JOB_STATE_')if you share code with the SDK. - Results are double-nested:
response.inlinedResponses.inlinedResponses[](also mirrored atmetadata.output.inlinedResponses.inlinedResponses[]). Each item is{response, metadata}or{error, metadata}; your requestmetadata.keycomes back so you can match results to inputs. batchStats.pendingRequestCountstayed at the request count even after SUCCEEDED, so do not use it as a progress signal.- A cancelled batch (
POST v1beta/batches/<id>:cancel) ends inBATCH_STATE_CANCELLEDwithdone: trueand a top-levelerror(code 13). - The create call returns the same Operation shape with
nameat top level andmetadata.state: BATCH_STATE_PENDING; persistnamebefore polling so a crash does not resubmit.
Observed 2026-10-26 with gemini-3.6-flash, 2 inline video requests, completion in about 2-3 minutes.