Upload a file source
September 29, 2026
Table of contents
Upload a file to a notebook as a new source. The file is sent as the raw request body with its MIME type in the Content-Type header (no multipart, no JSON). The notebook and the file name go in the query string.
The source starts with status preparing. Google then processes it (processing) until it is ready. Poll GET /notebooks/notebook to see when it is ready. In our tests every file type below was ready within 5 minutes. Chat and Studio use only ready sources.
To add web pages, YouTube videos, pasted text or Google Drive files use POST /sources.
https://api.useapi.net/v1/gemini-notebook/sources/upload?notebook=
notebook&name=name
Request Headers
Authorization: Bearer {API token}
Content-Type: {file MIME type} # see the table below
Content-Length: {file size in bytes}
API tokenis required, see Setup useapi.net for details.Content-Lengthis required. curl,fetchandrequestsset it for you when the body is a file or a buffer. A chunked upload without it returns411.Content-Typeis the file’s MIME type. It is used to pick the file extension whennameis omitted.
The request body is the file’s raw bytes. The API refuses files larger than 200 MB (209,715,200 bytes) with 413. Uploads over 100 MB are untested, and Cloudflare’s request-size limit may refuse them before they reach the API.
File types tested with this endpoint:
| File | Content-Type | Source kind |
|---|---|---|
.pdf | application/pdf | pdf |
.docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | docx |
.pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation | pptx |
.epub | application/epub+zip | epub |
.md | text/markdown | markdown |
.txt | text/plain | text |
.csv | text/csv | csv |
.mp3 | audio/mpeg | media |
.mp4 | video/mp4 | media |
.jpg | image/jpeg | image |
All kind and status values are listed in Source kinds and statuses.
Query Parameters
notebookis required, the notebook id returned by POST /notebooks or GET /notebooks. URL-encode it (it contains:and@). The id names its account, so noemailis needed.nameis optional, the file name with its extension, e.g.apollo11-overview.docx. Google uses it as the source title and types the file by its extension. URL-encode it.
Maximum length:200characters.
When omitted, the name isupload.<ext>with the extension taken fromContent-Type. That works for every Content-Type in the table above plusaudio/mp4(.m4a),audio/wav,image/pngandimage/webp. Any other Content-Type withoutnamereturns400.
Rename the source later with POST /sources/source.
Responses
-
The file was uploaded and Google is preparing it.
{ "notebook": "user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55", "source": "user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55:5a5455b1-0557-4fa4-a8a8-52f60dffa113", "title": "01-apollo11-wikipedia.pdf", "status": "preparing" }Once ready, GET /notebooks/
notebooklists it like this:{ "source": "user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55:5a5455b1-0557-4fa4-a8a8-52f60dffa113", "title": "01-apollo11-wikipedia.pdf", "kind": "pdf", "status": "ready", "words": 21578, "mimeType": "application/pdf", "created": "2026-09-27T23:24:53.000Z" } -
A missing or malformed
notebook, an empty body, or nonamewith a Content-Type we do not recognise.{ "error": "Pass ?name=<file name with extension> (or a Content-Type we recognise)", "code": 400 } -
Invalid API token.
{ "error": "useapi.net ⁝ Unauthorized", "code": 401 } -
The notebook id was issued to a different API token.
{ "error": "user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55 does not belong to this API token", "code": 403 } -
The account named in the notebook id is no longer configured, or Google no longer has the notebook.
{ "error": "Account [email protected] of user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55 is not configured", "code": 404 } -
{ "error": "Send the file as the request body with a Content-Length header", "code": 411 } -
The file is larger than the API’s
200 MBlimit. An upload over100 MBis untested and may be refused by Cloudflare before it reaches the API.{ "error": "File is 262144000 bytes, the limit is 209715200", "code": 413 } -
596 Account Error
Google reported the account as signed out. While we re-check it, the answer is the first message below. Retry in about a minute. If it stays signed out, the account is paused, you receive an email, and the answer becomes the second message until you re-add it via Setup Gemini Notebook.
{ "error": "Google reported this account as signed out. We are re-checking it — please retry in about a minute. If it stays signed out, you will receive a re-add email.", "code": 596 }{ "error": "Account [email protected]: Google signed this account out. Re-add it at https://useapi.net/docs/start-here/setup-gemini-notebook", "code": 596 } -
Google refused the upload. An upload is sent to Google once and is never retried by the API, so send it again. A failed upload may leave an unfinished source in the notebook — remove it with DELETE /sources/
source. A503means Google is temporarily unavailable, retry in a few minutes.{ "error": "Google request failed: upload failed: HTTP 400", "code": 502 }
Model
{ // TypeScript, all fields are optional
notebook: string // user:<id>-<email>-notebook:<uuid>
source: string // user:<id>-<email>-source:<notebook uuid>:<source uuid>
title: string // the file name Google stored
status: 'preparing' // poll GET /notebooks/{notebook} until the source is ready
}
Examples
-
NOTEBOOK="user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55" curl -X POST "https://api.useapi.net/v1/gemini-notebook/sources/upload?notebook=$(python3 -c "import urllib.parse,sys;print(urllib.parse.quote(sys.argv[1],safe=''))" "$NOTEBOOK")&name=apollo11-wikipedia.pdf" \ -H "Authorization: Bearer …" \ -H "Content-Type: application/pdf" \ --data-binary "@apollo11-wikipedia.pdf" -
import { readFile } from 'node:fs/promises'; const token = "API token"; const notebook = "user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55"; const name = "apollo11-wikipedia.pdf"; const qs = new URLSearchParams({ notebook, name }); const response = await fetch(`https://api.useapi.net/v1/gemini-notebook/sources/upload?${qs}`, { method: "POST", headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/pdf" }, body: await readFile(name) }); const result = await response.json(); console.log("response", {response, result}); -
import requests token = "API token" notebook = "user:[email protected]:d02c903b-17f8-4241-a263-2be9e7359d55" name = "apollo11-wikipedia.pdf" with open(name, "rb") as f: response = requests.post( "https://api.useapi.net/v1/gemini-notebook/sources/upload", params={"notebook": notebook, "name": name}, headers={"Authorization": f"Bearer {token}", "Content-Type": "application/pdf"}, data=f.read() ) print(response, response.json())