Python 88.3%
TypeScript 7.6%
Shell 4.1%
1# Gemini Files API — upload protocol, `files.*`, `files:register`, `generatedFiles`23**Status:** `DOCUMENTED` + `LIVE_VERIFIED` (resumable, multipart and raw uploads; get/list/delete; register of a public GCS object; generatedFiles list; 2026-09-18).4**Sources:** https://ai.google.dev/api/files · https://ai.google.dev/gemini-api/docs/generate-content/files · https://ai.google.dev/gemini-api/docs/generate-content/file-input-methods · discovery `media.upload` (`mediaUpload.protocols`), `File`, `RegisterFilesRequest`5**Machine-readable:** `generated/fragments/parameters/gemini-files.json`, `generated/fragments/endpoints/gemini-core.json` (files/media/generatedFiles), `generated/fragments/objects/gemini-core-objects.json#File`6**Last verified:** 2026-09-1878## 1. Limits and lifetime910| | Value |11|---|---|12| Per file | 2 GB (`mediaUpload.maxSize 2147483648`) |13| Per project | 20 GB |14| Retention | 48 h (`expirationTime` = create + 48 h observed), then auto-deleted |15| Cost | free, all regions |16| Download | user uploads cannot be downloaded (metadata only); generated files via `downloadUri` |17| When to use | total request > 20 MB (image/audio guides) / > 100 MB (file-input-methods; PDFs > 50 MB), or reuse across requests |1819## 2. Resumable upload — step by step (verified)2021**Step 1 — start session** (`POST https://generativelanguage.googleapis.com/upload/v1beta/files`, JSON metadata):2223```24x-goog-api-key: <key>25X-Goog-Upload-Protocol: resumable26X-Goog-Upload-Command: start27X-Goog-Upload-Header-Content-Length: 20028X-Goog-Upload-Header-Content-Type: text/plain29Content-Type: application/json3031{"file": {"display_name": "atlas-probe.txt"}}32```33→ `200`, **empty body**, response headers: `X-Goog-Upload-URL: https://generativelanguage.googleapis.com/upload/v1beta/files?upload_id=…&upload_protocol=resumable`, `X-Goog-Upload-Control-URL`, `X-Goog-Upload-Status: active`, `X-Goog-Upload-Chunk-Granularity: 8388608` (8 MiB).3435**Step 2 — send bytes** (`POST <X-Goog-Upload-URL>`):3637```38Content-Length: 20039X-Goog-Upload-Offset: 040X-Goog-Upload-Command: upload, finalize41<raw bytes>42```43→ `200 {"file": {...}}` + `X-Goog-Upload-Status: final`. For chunked uploads send `X-Goog-Upload-Command: upload` per chunk (offsets multiple of the granularity) and `finalize` last; `query`/`cancel` go to the control URL. The upload URL contains the session id, not your API key — but still keep it out of logs.4445**Alternatives (verified):** `POST /upload/v1beta/files?uploadType=multipart` with `Content-Type: multipart/related; boundary=…` (part 1 `application/json` metadata, part 2 the media) → 200; `POST /upload/v1beta/files?uploadType=media` with raw bytes and the media `Content-Type` → 200 (no display name).4647## 3. `File` object4849```json50{"name": "files/w7x6n2hlpxrq", "displayName": "atlas-probe.txt", "mimeType": "text/plain", "sizeBytes": "174",51 "createTime": "…", "updateTime": "…", "expirationTime": "2026-09-21T03:46:49.478571266Z",52 "sha256Hash": "ZmVhNTIx…", "uri": "https://generativelanguage.googleapis.com/v1beta/files/w7x6n2hlpxrq",53 "state": "ACTIVE", "source": "UPLOADED"}54```55`state` ∈ `PROCESSING | ACTIVE | FAILED` (+ `error: Status`) — poll `files.get` until ACTIVE for video/audio (tiny txt/PDF were ACTIVE immediately). `source` ∈ `UPLOADED | GENERATED | REGISTERED`. `videoMetadata.videoDuration` for videos. Custom ids: `file.name = "files/<id>"`, ≤ 40 chars `[a-z0-9-]`, no leading/trailing dash. `sha256Hash` is base64 of the hex digest string.5657## 4. Other methods5859| Method | Result (live) |60|---|---|61| `GET /v1beta/files?pageSize=&pageToken=` | `{"files": [...], "nextPageToken"?}`; also `/v1/files` |62| `GET /v1beta/files/{id}` | File; unknown or deleted → **403** `PERMISSION_DENIED You do not have permission to access the File <id> or it may not exist.` (never 404) |63| `DELETE /v1beta/files/{id}` | `{}`; later `fileData` use → 403 |64| `POST /v1beta/files:register` `{"uris": ["gs://bucket/object"]}` | registers GCS objects without copying; live with the public `gs://cloud-samples-data/generative-ai/image/scones.jpg` → `{"files": [{"name": "files/…", "displayName": "<id>", "mimeType": "image/jpeg", "sizeBytes": "394671", "uri": "…", "source": "REGISTERED"}]}` (no `state`/`expirationTime`; docs: valid up to 30 days, 2 GB/file, no storage cap). One failure fails the whole request. |65| `GET /v1beta/generatedFiles?pageSize=` | `{}` (none); items `{name: generatedFiles/…, mimeType, state: GENERATING|GENERATED|FAILED, error}`; `GET …/generatedFiles/{id}/operations/{op}` for LROs |6667## 5. Using a file6869```json70{"contents": [{"role": "user", "parts": [{"text": "What is the secret word?"}, {"fileData": {"fileUri": "<File.uri>", "mimeType": "text/plain"}}]}]}71```72`mimeType` optional (taken from the File). Verified: txt → `PELICAN`; 1-page PDF → `ATLAS` (536 prompt tokens: IMAGE 520 + TEXT 16; `countTokens` reports the same page as `DOCUMENT 560`).7374## 6. SDK75Python `client.files.upload(file=path|io, config=types.UploadFileConfig(mime_type=, display_name=, name=))`, `client.files.get(name=)`, `.list()`, `.delete(name=)`; Node `ai.files.upload({file, config:{mimeType, displayName}})`, `ai.files.get/list/delete`. SDKs implement the resumable protocol. Examples: `examples/gemini/files/` (curl script does the two-step protocol by hand).76