> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.privategpt.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.privategpt.dev/_mcp/server.

# Count tokens in a Message

POST /v1/messages/count_tokens
Content-Type: application/json

Count tokens in a Message

Reference: https://docs.privategpt.dev/api-reference/messages/count-message-tokens-v-1-messages-count-tokens-post

## Request

### Body (application/json)

This endpoint expects a CountTokensInput.

- `model` (string, required, default: default) — Model identifier or alias.
- `messages` (list of MessageInput, required) — Conversation messages for the request.
- `system` (list of System, optional) — System prompt input. Accepts str, list[str], System, list[System], or null. It is normalized internally to list[System].
- `tools` (list of ToolSpecBody, optional, nullable) — Optional tool definitions.
- `thinking` (Thinking, optional) — Thinking configuration.
- `tool_choice` (ToolChoice, optional) — Tool selection policy.
- `output_config` (OutputConfigInput, optional) — Optional output configuration options.
- `cache_control` (CountTokensInputCacheControl, optional, nullable) — Optional request-level cache control.

## Response

### 200

Successful Response

- `input_tokens` (integer, required) — Estimated number of input tokens for the provided payload.

## Errors

### 401 Unauthorized Error

Unauthorized

- `any`

### 422 Unprocessable Entity Error

Validation Error

- `detail` (list of ValidationError, optional) — detail field.

## Types

### MessageInput

Input message for AI conversations.

- `role` (enum, required) — The role of the message sender
  - Allowed values: `system`, `user`, `assistant`
- `content` (MessageInputContent, required) — The message content

### System

System message configuration for AI behavior and prompting.

- `text` (string, optional, nullable) — System prompt to use for the chat completion
- `citations` (Citations, optional) — Citation configuration for source attribution in responses
- `extensions` (list of enum, optional) — Set of enabled extensions
  - Allowed values: `zylon`
- `blob_visibility` (enum, optional) — Controls how blobs are exposed: binary (raw data), internal (private S3 URI), or public (public S3 URL)
  - Allowed values: `binary`, `internal`, `public`
- `prompt` (PromptConfig, optional) — Controls which platform-level prompt features are injected.
- `use_default_prompt` (boolean, optional, default: false, deprecated) — Deprecated: legacy toggle for built-in default prompt injection. Use system.prompt to control per-category prompt injection.

### ToolSpecBody

Definition for a tool the client can call.

- `name` (string, required) — Unique name identifier for the tool
- `type` (string, optional, nullable) — Type of the tool, use to identify internal tools database_query_v1 or semantic_search_v1
- `description` (string, optional, nullable) — Human-readable description of what the tool does
- `inputSchema` (map from string to any, optional, nullable) — JSON schema defining the input parameters the tool accepts
- `context` (list of ToolSpecBodyContextItems, optional, nullable) — Additional context or metadata for the tool
- `deferLoading` (boolean, optional, default: false) — When true, hide this tool from the model until at least one skill is loaded in the current conversation.
- `instructions` (string, optional, nullable) — Optional instructions injected into the system prompt when this tool is available. For internal tools a default template is used; providing a value here overrides that default. Set to an empty string to disable.

### Thinking

Configuration for AI reasoning and step-by-step thinking capabilities.

- `enabled` (boolean, optional, default: false) — Enable reasoning capabilities for the model, allowing it to think step-by-step
- `effort` (enum, optional, nullable, deprecated) — Deprecated. Use output_config.effort instead. Kept for backward compatibility with legacy clients.
  - Allowed values: `low`, `medium`, `high`, `max`, `xhigh`

### ToolChoice

Configuration for tool selection behavior during AI interactions.

- `type` (enum, optional, default: auto) — Tool selection strategy
  - Allowed values: `auto`, `any`, `tool`, `none`
- `name` (string, optional, nullable) — Name of the tool to use if not auto-selecting
- `disable_parallel_tool_use` (boolean, optional, default: false) — When true, prevents the AI from using multiple tools simultaneously
- `validation_mode` (enum, optional, default: lazy) — Tool validation mode. 'eager' validates tool calls before execution, 'lazy' validates if tool call is made.
  - Allowed values: `eager`, `lazy`

### OutputConfigInput

Output configuration shared across Anthropic-compatible request models.

- `effort` (enum, optional, nullable) — Reasoning effort level for output generation.
  - Allowed values: `low`, `medium`, `high`, `max`, `xhigh`
- `format` (JsonObjectFormat, optional, nullable) — Optional structured output format schema.

### CountTokensInputCacheControl

Optional request-level cache control.

- `type`: `ephemeral` (CacheControlEphemeral)
  - `ttl` (enum, optional, default: 5m) — ttl field.
    - Allowed values: `5m`, `1h`

### ValidationError

ValidationError schema.

- `loc` (list of ValidationErrorLocItems, required) — loc field.
- `msg` (string, required) — msg field.
- `type` (string, required) — type field.
- `input` (any, optional) — input field.
- `ctx` (ValidationErrorCtx, optional) — ctx field.

### MessageInputContent

The message content

### Citations

Configuration for citation generation in AI responses.

- `enabled` (boolean, optional, default: false) — Enable citations in responses
- `known_citations` (list of ZylonCitation, optional, nullable) — List of known citations to use in the response

### PromptConfig

Controls which platform-level prompt features are injected. These flags represent optional AI features adding internal instructions to the system prompt. All flags default to ``False`` — opt-in explicitly.

- `tools` (boolean, optional, default: false) — Enable per-tool instruction injection for all available tools.
- `citations` (boolean, optional, default: false) — Enable citation formatting guidelines injection.
- `thinking` (boolean, optional, default: false) — Enable thinking/reasoning guidelines when thinking is enabled.
- `code_execution` (boolean, optional, default: false) — Enable code execution environment instructions (filesystem layout, available paths) when any code execution tool is present.
- `skills` (boolean, optional, default: false) — Enable skill management instructions (when to load/unload skills, workflow guidance) when any skill management tool is present.

### ToolSpecBodyContextItems

- `type`: `file` (FileArtifact)
  - `value` (string, required) — Base64 encoded file content
- `type`: `ingested_artifact` (IngestedArtifact)
  - `context_filter` (ContextFilter, required) — Already processed ContextFilter
- `type`: `skill` (SkillArtifact)
  - `skill_filter` (SkillFilter, required) — Skill filter declaring the collection and optional skill_or_version_ids whitelist.
- `type`: `sql_database` (SqlDatabaseArtifact)
  - `connection_string` (string, required) — SQL database connection string
  - `description` (string, optional, default: ) — Optional description of the database
  - `enable_functions` (boolean, optional, default: true) — Whether to include functions in the inspection
  - `enable_procedures` (boolean, optional, default: true) — Whether to include stored procedures in the inspection
  - `enable_tables` (boolean, optional, default: true) — Whether to include tables in the inspection
  - `enable_views` (boolean, optional, default: true) — Whether to include views in the inspection
  - `schemas` (list of string, optional, nullable) — List of specific schemas to include, if None includes all non-system schemas
  - `ssl` (boolean, optional, default: false) — Whether to use SSL for the database connection
- `type`: `text` (TextArtifact)
  - `value` (string, required) — Plain text content
- `type`: `uri` (UriArtifact)
  - `value` (string, required) — URI to download from

### JsonObjectFormat

Structured JSON object format compatible with Anthropic output_config.format.

- `type` ("json_schema", required) — Output format type. Always "json_schema".
- `schema` (map from string to any, required) — JSON schema used to constrain the model output.

### ValidationErrorLocItems

### ValidationErrorCtx

ctx field.

### ZylonCitation

ZylonCitation represents a citation in the Zylon format.

- `id` (string, optional, nullable) — Unique identifier for the citation
- `index` (integer, optional, nullable) — Index of the citation in the document
- `artifact_id` (string, optional, nullable) — Identifier of the artifact associated with the citation
- `source_id` (string, optional, nullable) — Identifier of the source document from which the citation is derived

### ContextFilter

Filter by collection, artifacts and metadata in the ingested context. The main filter is the collection the context is part of. All artifacts ids are ensured to be part of the context. If any of the ids is not usable in the context (i.e. doesn't exist), the related task will fail. The metadata filter will look for all context matching the metadata, if any, and add it to the context. The filter is a dictionary of key-value pairs. The key is the metadata key, and the value is the metadata value. If both artifacts and metadata filters are provided, the result will be the intersection of the two filters.

- `collection` (string, optional, default: pgpt_collection) — The name of the collection to filter on.
- `artifacts` (list of string, optional, nullable) — Artifacts ids to filter on.
- `metadata_filter` (list of map from string to any, optional, nullable) — Metadata filter to apply on the context.

### SkillFilter

Collection-scoped filter used to resolve active skills and versions.

- `collection` (string, required) — Tenant collection boundary used to recover skills.
- `skill_or_version_ids` (list of string, optional, nullable) — Optional whitelist of identifiers inside collection. Each item may be a skill id (resolved to latest version) or a skill version id.

## Examples

**Request**

```json
{
  "model": "default",
  "messages": [
    {
      "role": "user",
      "content": "Count tokens for this input."
    }
  ],
  "system": [
    {
      "text": "You are a tokenizer."
    }
  ],
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a city.",
      "inputSchema": {
        "properties": {
          "city": {
            "description": "City name.",
            "type": "string"
          }
        },
        "required": [
          "city"
        ],
        "type": "object"
      }
    }
  ],
  "thinking": {
    "enabled": false
  },
  "tool_choice": {
    "type": "auto",
    "disable_parallel_tool_use": false,
    "validation_mode": "lazy"
  }
}
```

**Response**

```json
{
  "input_tokens": 1
}
```

**SDK Code**

```python
import requests

url = "https://api.example.com/v1/messages/count_tokens"

payload = {
    "model": "default",
    "messages": [
        {
            "role": "user",
            "content": "Count tokens for this input."
        }
    ],
    "system": [{ "text": "You are a tokenizer." }],
    "tools": [
        {
            "name": "get_weather",
            "description": "Get current weather for a city.",
            "inputSchema": {
                "properties": { "city": {
                        "description": "City name.",
                        "type": "string"
                    } },
                "required": ["city"],
                "type": "object"
            }
        }
    ],
    "thinking": { "enabled": False },
    "tool_choice": {
        "type": "auto",
        "disable_parallel_tool_use": False,
        "validation_mode": "lazy"
    }
}
headers = {"Content-Type": "application/json"}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.example.com/v1/messages/count_tokens';
const options = {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: '{"model":"default","messages":[{"role":"user","content":"Count tokens for this input."}],"system":[{"text":"You are a tokenizer."}],"tools":[{"name":"get_weather","description":"Get current weather for a city.","inputSchema":{"properties":{"city":{"description":"City name.","type":"string"}},"required":["city"],"type":"object"}}],"thinking":{"enabled":false},"tool_choice":{"type":"auto","disable_parallel_tool_use":false,"validation_mode":"lazy"}}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.example.com/v1/messages/count_tokens"

	payload := strings.NewReader("{\n  \"model\": \"default\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"Count tokens for this input.\"\n    }\n  ],\n  \"system\": [\n    {\n      \"text\": \"You are a tokenizer.\"\n    }\n  ],\n  \"tools\": [\n    {\n      \"name\": \"get_weather\",\n      \"description\": \"Get current weather for a city.\",\n      \"inputSchema\": {\n        \"properties\": {\n          \"city\": {\n            \"description\": \"City name.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"city\"\n        ],\n        \"type\": \"object\"\n      }\n    }\n  ],\n  \"thinking\": {\n    \"enabled\": false\n  },\n  \"tool_choice\": {\n    \"type\": \"auto\",\n    \"disable_parallel_tool_use\": false,\n    \"validation_mode\": \"lazy\"\n  }\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.example.com/v1/messages/count_tokens")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n  \"model\": \"default\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"Count tokens for this input.\"\n    }\n  ],\n  \"system\": [\n    {\n      \"text\": \"You are a tokenizer.\"\n    }\n  ],\n  \"tools\": [\n    {\n      \"name\": \"get_weather\",\n      \"description\": \"Get current weather for a city.\",\n      \"inputSchema\": {\n        \"properties\": {\n          \"city\": {\n            \"description\": \"City name.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"city\"\n        ],\n        \"type\": \"object\"\n      }\n    }\n  ],\n  \"thinking\": {\n    \"enabled\": false\n  },\n  \"tool_choice\": {\n    \"type\": \"auto\",\n    \"disable_parallel_tool_use\": false,\n    \"validation_mode\": \"lazy\"\n  }\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.example.com/v1/messages/count_tokens")
  .header("Content-Type", "application/json")
  .body("{\n  \"model\": \"default\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"Count tokens for this input.\"\n    }\n  ],\n  \"system\": [\n    {\n      \"text\": \"You are a tokenizer.\"\n    }\n  ],\n  \"tools\": [\n    {\n      \"name\": \"get_weather\",\n      \"description\": \"Get current weather for a city.\",\n      \"inputSchema\": {\n        \"properties\": {\n          \"city\": {\n            \"description\": \"City name.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"city\"\n        ],\n        \"type\": \"object\"\n      }\n    }\n  ],\n  \"thinking\": {\n    \"enabled\": false\n  },\n  \"tool_choice\": {\n    \"type\": \"auto\",\n    \"disable_parallel_tool_use\": false,\n    \"validation_mode\": \"lazy\"\n  }\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.example.com/v1/messages/count_tokens', [
  'body' => '{
  "model": "default",
  "messages": [
    {
      "role": "user",
      "content": "Count tokens for this input."
    }
  ],
  "system": [
    {
      "text": "You are a tokenizer."
    }
  ],
  "tools": [
    {
      "name": "get_weather",
      "description": "Get current weather for a city.",
      "inputSchema": {
        "properties": {
          "city": {
            "description": "City name.",
            "type": "string"
          }
        },
        "required": [
          "city"
        ],
        "type": "object"
      }
    }
  ],
  "thinking": {
    "enabled": false
  },
  "tool_choice": {
    "type": "auto",
    "disable_parallel_tool_use": false,
    "validation_mode": "lazy"
  }
}',
  'headers' => [
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.example.com/v1/messages/count_tokens");
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"model\": \"default\",\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"Count tokens for this input.\"\n    }\n  ],\n  \"system\": [\n    {\n      \"text\": \"You are a tokenizer.\"\n    }\n  ],\n  \"tools\": [\n    {\n      \"name\": \"get_weather\",\n      \"description\": \"Get current weather for a city.\",\n      \"inputSchema\": {\n        \"properties\": {\n          \"city\": {\n            \"description\": \"City name.\",\n            \"type\": \"string\"\n          }\n        },\n        \"required\": [\n          \"city\"\n        ],\n        \"type\": \"object\"\n      }\n    }\n  ],\n  \"thinking\": {\n    \"enabled\": false\n  },\n  \"tool_choice\": {\n    \"type\": \"auto\",\n    \"disable_parallel_tool_use\": false,\n    \"validation_mode\": \"lazy\"\n  }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Content-Type": "application/json"]
let parameters = [
  "model": "default",
  "messages": [
    [
      "role": "user",
      "content": "Count tokens for this input."
    ]
  ],
  "system": [["text": "You are a tokenizer."]],
  "tools": [
    [
      "name": "get_weather",
      "description": "Get current weather for a city.",
      "inputSchema": [
        "properties": ["city": [
            "description": "City name.",
            "type": "string"
          ]],
        "required": ["city"],
        "type": "object"
      ]
    ]
  ],
  "thinking": ["enabled": false],
  "tool_choice": [
    "type": "auto",
    "disable_parallel_tool_use": false,
    "validation_mode": "lazy"
  ]
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.example.com/v1/messages/count_tokens")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```