Upload a file source

September 29, 2026

Table of contents

  1. Request Headers
  2. Query Parameters
  3. Responses
  4. Model
  5. Examples
  6. Try It

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 token is required, see Setup useapi.net for details.
  • Content-Length is required. curl, fetch and requests set it for you when the body is a file or a buffer. A chunked upload without it returns 411.
  • Content-Type is the file’s MIME type. It is used to pick the file extension when name is 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
  • notebook is required, the notebook id returned by POST /notebooks or GET /notebooks. URL-encode it (it contains : and @). The id names its account, so no email is needed.
  • name is 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: 200 characters.
    When omitted, the name is upload.<ext> with the extension taken from Content-Type. That works for every Content-Type in the table above plus audio/mp4 (.m4a), audio/wav, image/png and image/webp. Any other Content-Type without name returns 400.

Rename the source later with POST /sources/source.

Responses
  • 201 Created

    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/notebook lists 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"
    }
    
  • 400 Bad Request

    A missing or malformed notebook, an empty body, or no name with a Content-Type we do not recognise.

    {
      "error": "Pass ?name=<file name with extension> (or a Content-Type we recognise)",
      "code": 400
    }
    
  • 401 Unauthorized

    Invalid API token.

    {
      "error": "useapi.net ⁝ Unauthorized",
      "code": 401
    }
    
  • 403 Forbidden

    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
    }
    
  • 404 Not Found

    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
    }
    
  • 411 Length Required

    {
      "error": "Send the file as the request body with a Content-Length header",
      "code": 411
    }
    
  • 413 Content Too Large

    The file is larger than the API’s 200 MB limit. An upload over 100 MB is 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
    }
    
  • 502 Bad Gateway

    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. A 503 means 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())
    
Try It