Repository Public
1Gemini and other generative AI models process input and output at a granularity2called a *token*.34**For Gemini models, a token is equivalent to about 4 characters.5100 tokens is equal to about 60-80 English words.**67## About tokens89Tokens can be single characters like `z` or whole words like `cat`. Long words10are broken up into several tokens. The set of all tokens used by the model is11called the vocabulary, and the process of splitting text into tokens is called12*tokenization*.1314When billing is enabled, the [cost of a call to the Gemini API](https://ai.google.dev/pricing) is15determined in part by the number of input and output tokens, so knowing how to16count tokens can be helpful.1718You can try out counting tokens in our Colab.1920|---|---|---|21| [View on ai.google.dev](https://ai.google.dev/gemini-api/docs/tokens) | [Try a Colab notebook](https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/Counting_Tokens.ipynb) | [View notebook on GitHub](https://colab.research.google.com/github/google-gemini/cookbook/blob/main/quickstarts/Counting_Tokens.ipynb) |2223## Count tokens2425All input to and output from the Gemini API is tokenized, including text, image26files, and other non-text modalities.2728You can count tokens in the following ways:2930- **Call [`count_tokens`](https://ai.google.dev/api/rest/v1/models/countTokens) with the input31 of the request.** 32 This returns the total number of tokens in *the33 input only*. You can make this call before sending the input to the34 model to check the size of your requests.3536- **Use the `usage_metadata` attribute on the `response` object after37 calling `generate_content`.** 38 This returns the total number of39 tokens in *both the input and the output* : `total_token_count`. 40 It41 also returns the token counts of the input and output separately:42 `prompt_token_count` (input tokens) and `candidates_token_count`43 (output tokens).4445 If you are using a [thinking46 model](https://ai.google.dev/gemini-api/docs/thinking), the tokens used during the thinking47 process are returned in `thoughts_token_count`. And if you are using48 [Context caching](https://ai.google.dev/gemini-api/docs/caching), the cached token49 count will be in `cached_content_token_count`.5051### Count text tokens5253If you call `count_tokens` with a text-only input, it returns the token count of54the text in *the input only* (`total_tokens`). You can make this call before55calling `generate_content` to check the size of your requests.5657Another option is calling `generate_content` and then using the `usage_metadata`58attribute on the `response` object to get the following:5960- The separate token counts of the input (`prompt_token_count`), the cached content (`cached_content_token_count`) and the output (`candidates_token_count`)61- The token count for the thinking process (`thoughts_token_count`)62- The total number of tokens in *both the input and the output*63 (`total_token_count`)6465### Python6667 from google import genai6869 client = genai.Client()70 prompt = "The quick brown fox jumps over the lazy dog."7172 total_tokens = client.models.count_tokens(73 model="gemini-3.8-flash", contents=prompt74 )75 print("total_tokens: ", total_tokens)7677 response = client.models.generate_content(78 model="gemini-3.8-flash", contents=prompt79 )8081 print(response.usage_metadata)8283### JavaScript8485 import { GoogleGenAI } from '@google/genai';8687 const ai = new GoogleGenAI({});88 const prompt = "The quick brown fox jumps over the lazy dog.";8990 async function main() {91 const countTokensResponse = await ai.models.countTokens({92 model: "gemini-3.8-flash",93 contents: prompt,94 });95 console.log(countTokensResponse.totalTokens);9697 const generateResponse = await ai.models.generateContent({98 model: "gemini-3.8-flash",99 contents: prompt,100 });101 console.log(generateResponse.usageMetadata);102 }103104 await main();105106### Go107108 ctx := context.Background()109 client, err := genai.NewClient(ctx, nil)110111 // Convert prompt to a slice of *genai.Content using the helper.112 contents := []*genai.Content{113 genai.NewContentFromText(prompt, genai.RoleUser),114 }115 countResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)116 if err != nil {117 return err118 }119 fmt.Println("total_tokens:", countResp.TotalTokens)120121 response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)122 if err != nil {123 log.Fatal(err)124 }125 usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", " ")126 if err != nil {127 log.Fatal(err)128 }129 fmt.Println(string(usageMetadata))130 ```131132### Count multi-turn (chat) tokens133134If you call `count_tokens` with the chat history, it returns the total token135count of the text from each role in the chat (`total_tokens`).136137Another option is calling `send_message` and then using the `usage_metadata`138attribute on the `response` object to get the following:139140- The separate token counts of the input (`prompt_token_count`), the cached content (`cached_content_token_count`) and the output (`candidates_token_count`)141- The token count for the thinking process (`thoughts_token_count`)142- The total number of tokens in *both the input and the output* (`total_token_count`)143144To understand how big your next conversational turn will be, you need to append145it to the history when you call `count_tokens`.146147### Python148149 from google import genai150 from google.genai import types151152 client = genai.Client()153154 chat = client.chats.create(155 model="gemini-3.8-flash",156 history=[157 types.Content(158 role="user", parts=[types.Part(text="Hi my name is Bob")]159 ),160 types.Content(role="model", parts=[types.Part(text="Hi Bob!")]),161 ],162 )163164 print(165 client.models.count_tokens(166 model="gemini-3.8-flash", contents=chat.get_history()167 )168 )169170 response = chat.send_message(171 message="In one sentence, explain how a computer works to a young child."172 )173 print(response.usage_metadata)174175 extra = types.UserContent(176 parts=[177 types.Part(178 text="What is the meaning of life?",179 )180 ]181 )182 history = [*chat.get_history(), extra]183 print(client.models.count_tokens(model="gemini-3.8-flash", contents=history))184185### JavaScript186187 import { GoogleGenAI } from '@google/genai';188189 const ai = new GoogleGenAI({});190191 async function main() {192 const history = [193 { role: "user", parts: [{ text: "Hi my name is Bob" }] },194 { role: "model", parts: [{ text: "Hi Bob!" }] },195 ];196 const chat = ai.chats.create({197 model: "gemini-3.8-flash",198 history: history,199 });200201 const countTokensResponse = await ai.models.countTokens({202 model: "gemini-3.8-flash",203 contents: chat.getHistory(),204 });205 console.log(countTokensResponse.totalTokens);206207 const chatResponse = await chat.sendMessage({208 message: "In one sentence, explain how a computer works to a young child.",209 });210 console.log(chatResponse.usageMetadata);211212 const extraMessage = {213 role: "user",214 parts: [{ text: "What is the meaning of life?" }],215 };216 const combinedHistory = [...chat.getHistory(), extraMessage];217 const combinedCountTokensResponse = await ai.models.countTokens({218 model: "gemini-3.8-flash",219 contents: combinedHistory,220 });221 console.log(222 "Combined history token count:",223 combinedCountTokensResponse.totalTokens,224 );225 }226227 await main();228229### Go230231 ctx := context.Background()232 client, err := genai.NewClient(ctx, nil)233234 history := []*genai.Content{235 {Role: genai.RoleUser, Parts: []*genai.Part({Text: "Hi my name is Bob"})},236 {Role: genai.RoleModel, Parts: []*genai.Part({Text: "Hi Bob!"})},237 }238 chat, err := client.Chats.Create(ctx, "gemini-3.8-flash", nil, history)239 if err != nil {240 log.Fatal(err)241 }242243 firstTokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", chat.History(false), nil)244 if err != nil {245 log.Fatal(err)246 }247 fmt.Println(firstTokenResp.TotalTokens)248249 resp, err := chat.SendMessage(ctx, genai.NewPartFromText("In one sentence, explain how a computer works to a young child."))250 if err != nil {251 log.Fatal(err)252 }253 fmt.Printf("%#v\n", resp.UsageMetadata)254255 extra := genai.NewContentFromText("What is the meaning of life?", genai.RoleUser)256 hist := chat.History(false)257 hist = append(hist, extra)258259 secondTokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", hist, nil)260 if err != nil {261 log.Fatal(err)262 }263 fmt.Println(secondTokenResp.TotalTokens)264265### Count multimodal tokens266267All input to the Gemini API is tokenized, including text, image files, and other268non-text modalities. Note the following high-level key points about tokenization269of multimodal input during processing by the Gemini API:270271- Image inputs with both dimensions \<=384 pixels are counted as272 258 tokens. Images larger in one or both dimensions are cropped and scaled as273 needed into tiles of 768x768 pixels, each counted as 258 tokens.274275- Video and audio files are converted to tokens at the following fixed rates:276 video at 263 tokens per second and audio at 32 tokens per second. This rate277 applies to static processing (default). For agentic processing, token usage278 varies. See [Video token usage by processing mode](https://ai.google.dev/gemini-api/docs/generate-content/tokens#video-token-usage).279280#### Media resolutions281282[Gemini 3 models](https://ai.google.dev/gemini-api/docs/models#gemini-3) introduce granular control over283multimodal vision processing with the `media_resolution` parameter. The284`media_resolution` parameter determines the285**maximum number of tokens allocated per input image or video frame.**286Higher resolutions improve the model's ability to287read fine text or identify small details, but increase token usage and latency.288289For more details about the parameter and how it can impact token calculations,290see the [media resolution](https://ai.google.dev/gemini-api/docs/generate-content/media-resolution) guide.291292#### Image files293294If you call `count_tokens` with a text-and-image input, it returns the combined295token count of the text and the image in *the input only* (`total_tokens`). You296can make this call before calling `generate_content` to check the size of your297requests. You can also optionally call `count_tokens` on the text and the file298separately.299300Another option is calling `generate_content` and then using the `usage_metadata`301attribute on the `response` object to get the following:302303- The separate token counts of the input (`prompt_token_count`), the cached content (`cached_content_token_count`) and the output (`candidates_token_count`)304- The token count for the thinking process (`thoughts_token_count`)305- The total number of tokens in *both the input and the output* (`total_token_count`)306307Example that uses an uploaded image from the File API:308309### Python310311 from google import genai312313 client = genai.Client()314 prompt = "Tell me about this image"315 your_image_file = client.files.upload(file=media / "organ.jpg")316317 print(318 client.models.count_tokens(319 model="gemini-3.8-flash", contents=[prompt, your_image_file]320 )321 )322323 response = client.models.generate_content(324 model="gemini-3.8-flash", contents=[prompt, your_image_file]325 )326 print(response.usage_metadata)327328### JavaScript329330 import { GoogleGenAI } from '@google/genai';331332 const ai = new GoogleGenAI({});333 const prompt = "Tell me about this image";334335 async function main() {336 const organ = await ai.files.upload({337 file: path.join(media, "organ.jpg"),338 config: { mimeType: "image/jpeg" },339 });340341 const countTokensResponse = await ai.models.countTokens({342 model: "gemini-3.8-flash",343 contents: createUserContent([344 prompt,345 createPartFromUri(organ.uri, organ.mimeType),346 ]),347 });348 console.log(countTokensResponse.totalTokens);349350 const generateResponse = await ai.models.generateContent({351 model: "gemini-3.8-flash",352 contents: createUserContent([353 prompt,354 createPartFromUri(organ.uri, organ.mimeType),355 ]),356 });357 console.log(generateResponse.usageMetadata);358 }359360 await main();361362### Go363364 ctx := context.Background()365 client, err := genai.NewClient(ctx, nil)366367 file, err := client.Files.UploadFromPath(368 ctx, 369 filepath.Join(getMedia(), "organ.jpg"), 370 &genai.UploadFileConfig{371 MIMEType : "image/jpeg",372 },373 )374 if err != nil {375 log.Fatal(err)376 }377 parts := []*genai.Part{378 genai.NewPartFromText("Tell me about this image"),379 genai.NewPartFromURI(file.URI, file.MIMEType),380 }381 contents := []*genai.Content{382 genai.NewContentFromParts(parts, genai.RoleUser),383 }384385 tokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)386 if err != nil {387 log.Fatal(err)388 }389 fmt.Println("Multimodal image token count:", tokenResp.TotalTokens)390391 response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)392 if err != nil {393 log.Fatal(err)394 }395 usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", " ")396 if err != nil {397 log.Fatal(err)398 }399 fmt.Println(string(usageMetadata))400401Example that provides the image as inline data:402403### Python404405 from google import genai406 import PIL.Image407408 client = genai.Client()409 prompt = "Tell me about this image"410 your_image_file = PIL.Image.open(media / "organ.jpg")411412 print(413 client.models.count_tokens(414 model="gemini-3.8-flash", contents=[prompt, your_image_file]415 )416 )417418 response = client.models.generate_content(419 model="gemini-3.8-flash", contents=[prompt, your_image_file]420 )421 print(response.usage_metadata)422423### JavaScript424425 import { GoogleGenAI } from '@google/genai';426427 const ai = new GoogleGenAI({});428 const prompt = "Tell me about this image";429 const imageBuffer = fs.readFileSync(path.join(media, "organ.jpg"));430431 const imageBase64 = imageBuffer.toString("base64");432433 const contents = createUserContent([434 prompt,435 createPartFromBase64(imageBase64, "image/jpeg"),436 ]);437438 async function main() {439 const countTokensResponse = await ai.models.countTokens({440 model: "gemini-3.8-flash",441 contents: contents,442 });443 console.log(countTokensResponse.totalTokens);444445 const generateResponse = await ai.models.generateContent({446 model: "gemini-3.8-flash",447 contents: contents,448 });449 console.log(generateResponse.usageMetadata);450 }451452 await main();453454### Go455456 ctx := context.Background()457 client, err := genai.NewClient(ctx, nil)458459 imageBytes, err := os.ReadFile("organ.jpg")460 if err != nil {461 log.Fatalf("Failed to read image file: %v", err)462 }463 parts := []*genai.Part{464 genai.NewPartFromText("Tell me about this image"),465 {466 InlineData: &genai.Blob{467 MIMEType: "image/jpeg",468 Data: imageBytes,469 },470 },471 }472 contents := []*genai.Content{473 genai.NewContentFromParts(parts, genai.RoleUser),474 }475476 tokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)477 if err != nil {478 log.Fatal(err)479 }480 fmt.Println("Multimodal image token count:", tokenResp.TotalTokens)481482 response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)483 if err != nil {484 log.Fatal(err)485 }486 usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", " ")487 if err != nil {488 log.Fatal(err)489 }490 fmt.Println(string(usageMetadata))491492#### Video or audio files493494Audio and video are each converted to tokens at the following fixed rates:495496- Video: 263 tokens per second497- Audio: 32 tokens per second498499If you call `count_tokens` with a text-and-video/audio input, it returns the500combined token count of the text and the video/audio file in *the input only*501(`total_tokens`). You can make this call before calling `generate_content` to502check the size of your requests. You can also optionally call `count_tokens` on503the text and the file separately.504505Another option is calling `generate_content` and then using the `usage_metadata`506attribute on the `response` object to get the following:507508- The separate token counts of the input (`prompt_token_count`), the cached content (`cached_content_token_count`) and the output (`candidates_token_count`)509- The token count for the thinking process (`thoughts_token_count`)510- The total number of tokens in *both the input and the output*511 (`total_token_count`).512513### Python514515 from google import genai516 import time517518 client = genai.Client()519 prompt = "Tell me about this video"520 your_file = client.files.upload(file=media / "Big_Buck_Bunny.mp4")521522 while not your_file.state or your_file.state.name != "ACTIVE":523 print("Processing video...")524 print("File state:", your_file.state)525 time.sleep(5)526 your_file = client.files.get(name=your_file.name)527528 print(529 client.models.count_tokens(530 model="gemini-3.8-flash", contents=[prompt, your_file]531 )532 )533534 response = client.models.generate_content(535 model="gemini-3.8-flash", contents=[prompt, your_file]536 )537 print(response.usage_metadata)538539### JavaScript540541 import { GoogleGenAI } from '@google/genai';542543 const ai = new GoogleGenAI({});544 const prompt = "Tell me about this video";545546 async function main() {547 let videoFile = await ai.files.upload({548 file: path.join(media, "Big_Buck_Bunny.mp4"),549 config: { mimeType: "video/mp4" },550 });551552 while (!videoFile.state || videoFile.state.toString() !== "ACTIVE") {553 console.log("Processing video...");554 console.log("File state: ", videoFile.state);555 await sleep(5000);556 videoFile = await ai.files.get({ name: videoFile.name });557 }558559 const countTokensResponse = await ai.models.countTokens({560 model: "gemini-3.8-flash",561 contents: createUserContent([562 prompt,563 createPartFromUri(videoFile.uri, videoFile.mimeType),564 ]),565 });566 console.log(countTokensResponse.totalTokens);567568 const generateResponse = await ai.models.generateContent({569 model: "gemini-3.8-flash",570 contents: createUserContent([571 prompt,572 createPartFromUri(videoFile.uri, videoFile.mimeType),573 ]),574 });575 console.log(generateResponse.usageMetadata);576 }577578 await main();579580### Go581582 ctx := context.Background()583 client, err := genai.NewClient(ctx, nil)584585 file, err := client.Files.UploadFromPath(586 ctx,587 filepath.Join(getMedia(), "Big_Buck_Bunny.mp4"),588 &genai.UploadFileConfig{589 MIMEType : "video/mp4",590 },591 )592 if err != nil {593 log.Fatal(err)594 }595596 for file.State == genai.FileStateUnspecified || file.State != genai.FileStateActive {597 fmt.Println("Processing video...")598 fmt.Println("File state:", file.State)599 time.Sleep(5 * time.Second)600601 file, err = client.Files.Get(ctx, file.Name, nil)602 if err != nil {603 log.Fatal(err)604 }605 }606607 parts := []*genai.Part{608 genai.NewPartFromText("Tell me about this video"),609 genai.NewPartFromURI(file.URI, file.MIMEType),610 }611 contents := []*genai.Content{612 genai.NewContentFromParts(parts, genai.RoleUser),613 }614615 tokenResp, err := client.Models.CountTokens(ctx, "gemini-3.8-flash", contents, nil)616 if err != nil {617 log.Fatal(err)618 }619 fmt.Println("Multimodal video/audio token count:", tokenResp.TotalTokens)620 response, err := client.Models.GenerateContent(ctx, "gemini-3.8-flash", contents, nil)621 if err != nil {622 log.Fatal(err)623 }624 usageMetadata, err := json.MarshalIndent(response.UsageMetadata, "", " ")625 if err != nil {626 log.Fatal(err)627 }628 fmt.Println(string(usageMetadata))629630#### Video token usage by processing mode631632Token usage for video depends on the processing mode:633634| **Processing mode** | **Token calculation** | **Typical usage** |635|---|---|---|636| **Static** (default) | \~100 tokens/second by default (low resolution) or \~300 tokens/second (high resolution). All frames sampled at 1 FPS. | Predictable, proportional to video duration. |637| **Agentic** | Varies by content complexity. The model loads only the transcript and/or frames and/or audio needed to answer the prompt. | Up to 88% fewer tokens for long-form content. |638639With agentic processing, a 1-hour lecture that would use \~1.08M tokens in static mode might use \~108K tokens, depending on the prompt and content.640641To check actual token usage for a request, use the `usage_metadata` field in the response. In `UsageMetadata`, agentic video tokens are reported across the following fields:642643- **Initial prompt** (video reference + user prompt): `prompt_token_count`644- **Navigation thinking** : `thoughts_token_count`645- **Transcript, frames, and audio loaded on demand** : `tool_use_prompt_token_count`646- **Final answer** : `candidates_token_count`647648### Count thought tokens649650> [!NOTE]651> **Note:** The API provides **summaries** in both the [free and paid tiers](https://ai.google.dev/gemini-api/docs/pricing). **Thought signatures** increase the input token count (and your cost) when you send them back in subsequent conversational turns.652653When you turn on thinking, response pricing is the sum of output tokens and thinking tokens. You can retrieve the total number of generated thinking tokens from the `thoughtsTokenCount` field (or SDK equivalent).654655### Python656657 # ...658 print("Thoughts tokens:", response.usage_metadata.thoughts_token_count)659 print("Output tokens:", response.usage_metadata.candidates_token_count)660661### JavaScript662663 // ...664 console.log(`Thoughts tokens: ${response.usageMetadata.thoughtsTokenCount}`);665 console.log(`Output tokens: ${response.usageMetadata.candidatesTokenCount}`);666667### Go668669 // ...670 fmt.Println("Thoughts tokens:", response.UsageMetadata.ThoughtsTokenCount)671 fmt.Println("Output tokens:", response.UsageMetadata.CandidatesTokenCount)672673Thinking models generate full thoughts to improve the quality of the final response, and then output [summaries](https://ai.google.dev/gemini-api/docs/thinking#summaries) to provide insight into the thought process. So, the API bases pricing on the full thought tokens the model generates to create a summary, even though the API only outputs the summary.674675You can learn more about how to configure thinking in the [Gemini thinking](https://ai.google.dev/gemini-api/docs/thinking) guide.676677## Context windows678679The models available through the Gemini API have context windows that are680measured in tokens. The context window defines how much input you can provide681and how much output the model can generate. You can determine the size of the682context window by calling the [`models.get` endpoint](https://ai.google.dev/api/rest/v1/models/get)683or by looking in the [models documentation](https://ai.google.dev/gemini-api/docs/models).684685### Python686687 from google import genai688689 client = genai.Client()690 model_info = client.models.get(model="gemini-3.8-flash")691 print(f"{model_info.input_token_limit=}")692 print(f"{model_info.output_token_limit=}")693694### JavaScript695696 import { GoogleGenAI } from '@google/genai';697698 const ai = new GoogleGenAI({});699700 async function main() {701 const modelInfo = await ai.models.get({model: 'gemini-3.8-flash'});702 console.log(modelInfo.inputTokenLimit);703 console.log(modelInfo.outputTokenLimit);704 }705706 await main();707708### Go709710 ctx := context.Background()711 client, err := genai.NewClient(ctx, nil)712 if err != nil {713 log.Fatal(err)714 }715 modelInfo, err := client.ModelInfo(ctx, "models/gemini-3.8-flash")716 if err != nil {717 log.Fatal(err)718 }719 fmt.Println("input token limit:", modelInfo.InputTokenLimit)720 fmt.Println("output token limit:", modelInfo.OutputTokenLimit)