Log Query
curl --request GET \
--url https://client.tennda.ai/prod-api/api/logs/openLogimport requests
url = "https://client.tennda.ai/prod-api/api/logs/openLog"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://client.tennda.ai/prod-api/api/logs/openLog', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://client.tennda.ai/prod-api/api/logs/openLog",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://client.tennda.ai/prod-api/api/logs/openLog"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://client.tennda.ai/prod-api/api/logs/openLog")
.asString();require 'uri'
require 'net/http'
url = URI("https://client.tennda.ai/prod-api/api/logs/openLog")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyInterface Modules
Log Query
Paginated log pull by time and type for calls, consumption, top-ups, and more
GET
/
prod-api
/
api
/
logs
/
openLog
Log Query
curl --request GET \
--url https://client.tennda.ai/prod-api/api/logs/openLogimport requests
url = "https://client.tennda.ai/prod-api/api/logs/openLog"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://client.tennda.ai/prod-api/api/logs/openLog', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://client.tennda.ai/prod-api/api/logs/openLog",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://client.tennda.ai/prod-api/api/logs/openLog"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://client.tennda.ai/prod-api/api/logs/openLog")
.asString();require 'uri'
require 'net/http'
url = URI("https://client.tennda.ai/prod-api/api/logs/openLog")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyOverview
Fetch business logs visible to the current credential. Covers consumption, top-up, error, refund, admin, and test-credit records — useful for reconciliation, troubleshooting, and usage analytics.Max 31 days per request
The span between
startTime and endTime must not exceed 31 days. Time format is always yyyy-MM-dd HH:mm:ss.Pagination defaults
Defaults:
page=1, pageSize=30. pageSize max is 1000.Endpoint
| Item | Value |
|---|---|
| Method | GET |
| URL | https://client.tennda.ai/prod-api/api/logs/openLog |
| Auth header | Authorization |
| Language header | Accept-Language (zh-CN / en-US; affects billingProcessText) |
Authentication
Both header formats are accepted:Authorization: Bearer sk-your_api_key
Authorization: sk-your_api_key
| Credential | Visible scope | tokenName |
|---|---|---|
| System access token | All related logs for that user | Optional; fuzzy match |
| API Key | Logs for the current Key only | Ignored; server forces an exact filter on the current Key |
Never put a full key in URLs, frontend code, logs, or support tickets.
Request parameters
Pagination
integer
default:"1"
Page number; values
≤ 0 are treated as 1integer
default:"30"
Page size; values
≤ 0 are treated as 30; max 1000Filters
string
API Key name. Only effective with system access token auth (fuzzy match); ignored when authenticating with an API Key
string
Model name, exact match; comma-separated for multiple models
string
Request ID, exact match
string
Log types, comma-separated, e.g.
2,5types value | Meaning |
|---|---|
1 | Top-up |
2 | Consumption |
3 | Admin |
5 | Error |
6 | Refund |
8 | Test credit |
Time range
string
Start time,
yyyy-MM-dd HH:mm:ss, inclusivestring
End time,
yyyy-MM-dd HH:mm:ss, inclusiveAccept-Language only affects the language of billingProcessText: zh-CN / omitted → Chinese, en-US → English.Request examples
- curl
- JavaScript
- Python
curl --get 'https://client.tennda.ai/prod-api/api/logs/openLog' \
--header 'Authorization: Bearer your_api_key' \
--header 'Accept-Language: en-US' \
--data-urlencode 'page=1' \
--data-urlencode 'pageSize=30' \
--data-urlencode 'startTime=2026-07-01 00:00:00' \
--data-urlencode 'endTime=2026-07-21 23:59:59' \
--data-urlencode 'types=2,5'
const params = new URLSearchParams({
page: '1',
pageSize: '30',
startTime: '2026-07-01 00:00:00',
endTime: '2026-07-21 23:59:59',
types: '2,5',
});
const response = await fetch(
`https://client.tennda.ai/prod-api/api/logs/openLog?${params}`,
{
method: 'GET',
headers: {
Authorization: 'Bearer your_api_key',
'Accept-Language': 'en-US',
},
},
);
const result = await response.json();
if (result.code !== 200) {
throw new Error(result.msg);
}
console.log(result.data.total, result.data.rows);
import requests
url = "https://client.tennda.ai/prod-api/api/logs/openLog"
headers = {
"Authorization": "Bearer your_api_key",
"Accept-Language": "en-US",
}
params = {
"page": 1,
"pageSize": 30,
"startTime": "2026-07-01 00:00:00",
"endTime": "2026-07-21 23:59:59",
"types": "2,5",
}
response = requests.get(url, headers=headers, params=params, timeout=30)
response.raise_for_status()
result = response.json()
if result.get("code") != 200:
raise RuntimeError(result.get("msg"))
print("total:", result["data"]["total"])
for item in result["data"]["rows"]:
print(item["requestId"], item["modelName"], item["quotaDollar"])
Success response
{
"code": 200,
"msg": "操作成功",
"data": {
"total": 1,
"rows": [
{
"id": "7485162150730129409",
"createdAt": 1784601721,
"createTime": "2026-07-21 10:42:01",
"type": 2,
"requestId": "request_example_001",
"ip": "203.0.113.10",
"requestPath": "/v1/messages",
"isStream": 1,
"streamStatus": "正常 (eof)",
"useTime": 2,
"firstTokenTime": 1043,
"modelName": "claude-sonnet-5",
"tokenName": "default",
"quotaDollar": "0.006098",
"billingType": "token_ratio",
"billingCountMode": "上游返回",
"requestConversion": "Claude Messages",
"textInputTokens": 2804,
"textOutputTokens": 49,
"cacheTokens": 0,
"billingProcessText": "Input price: ..., final charge: $0.006098"
}
]
}
}
quotaDollar is a decimal string. Parse it with Decimal / BigDecimal — avoid binary floating-point types.Envelope fields
| Field | Type | Description |
|---|---|---|
code | integer | 200 success, 401 auth failure |
msg | string | Status message |
data.total | integer | Total matching rows (not page count) |
data.rows | array | Current page records |
Log row (common fields)
| Field | Type | Description |
|---|---|---|
id | string | Log ID; treat as a string |
createdAt | integer | Unix timestamp (seconds) |
createTime | string | Formatted created time |
type | integer | Log type; see table above |
requestId | string | null | Request ID |
ip | string | null | Request IP |
requestPath | string | null | Request path |
isStream | integer | null | Whether the call was streaming |
streamStatus | string | null | Stream status |
useTime | integer | null | Total duration (seconds) |
firstTokenTime | integer | null | Time to first token (ms) |
modelName | string | null | Model name |
tokenName | string | null | Token / Key name |
quotaDollar | string | null | Customer charge (USD) |
billingType | string | null | Billing type |
billingCountMode | string | null | Billing count mode |
requestConversion | string | null | Protocol conversion type |
textInputTokens | integer | null | Text input tokens |
textOutputTokens | integer | null | Text output tokens |
cacheCreationTokens5m | integer | null | 5-minute cache creation tokens |
cacheCreationTokens1h | integer | null | 1-hour cache creation tokens |
cacheTokens | integer | null | Cache hit tokens |
billingProcessText | string | null | Billing breakdown text (language follows the header) |
Multimedia and other metering fields
Multimedia and other metering fields
| Field | Type | Description |
|---|---|---|
audioInput / audioOutput | integer | null | Audio in/out metering |
imageInputTokens / imageOutputTokens | integer | null | Image tokens |
videoOutputTokens | integer | null | Video output tokens |
imageCount | integer | null | Image count |
videoResolution | string | null | Video resolution |
toolCallBilling | array | null | Tool-call billing details |
Error responses
| Scenario | code | Typical msg |
|---|---|---|
| Missing or invalid Authorization | 401 | Authorization header missing or invalid |
| Empty key | 401 | Authorization key is empty |
| Invalid or disabled key | 401 | 无效或已禁用的密钥 |
| Bad time format | 500 | startTime 格式错误,请使用 yyyy-MM-dd HH:mm:ss |
| Time range too large | 500 | 时间范围不能超过1个月 |
Integration tips
- Keep credentials on the server; never log full keys in application logs.
- Keep each query within 31 days; page through longer history in segments.
- Derive page count as
ceil(total / pageSize); do not treattotalas a page number. - Check response body
code; HTTP 200 alone does not mean business success. - Parse amount fields as decimal strings; do not recompute with
float/number.
