Bill Query
curl --request GET \
--url https://client.tennda.ai/prod-api/api/logs/v2/getDailyListimport requests
url = "https://client.tennda.ai/prod-api/api/logs/v2/getDailyList"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://client.tennda.ai/prod-api/api/logs/v2/getDailyList', 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/v2/getDailyList",
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/v2/getDailyList"
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/v2/getDailyList")
.asString();require 'uri'
require 'net/http'
url = URI("https://client.tennda.ai/prod-api/api/logs/v2/getDailyList")
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
Bill Query
Pull day-level consumption details and summaries from TenndaAI for reconciliation, cost analysis, and bill collection
GET
/
prod-api
/
api
/
logs
/
v2
/
getDailyList
Bill Query
curl --request GET \
--url https://client.tennda.ai/prod-api/api/logs/v2/getDailyListimport requests
url = "https://client.tennda.ai/prod-api/api/logs/v2/getDailyList"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://client.tennda.ai/prod-api/api/logs/v2/getDailyList', 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/v2/getDailyList",
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/v2/getDailyList"
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/v2/getDailyList")
.asString();require 'uri'
require 'net/http'
url = URI("https://client.tennda.ai/prod-api/api/logs/v2/getDailyList")
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
Two read-only billing endpoints: day summaries for net spend trends, and line items broken down by model × token type. Amounts are alwaysUSD, billing timezone is utc+8, and data is T+1.
Consumption details
getDailyList: grain (bill day × account × model × token type). Max span 92 days per request; pageSize capped at 100.Day summary
getDailySummary: grain (bill day × account). Max span 366 days per request; pageSize capped at 400.Endpoints
| Endpoint | Method / path | Use case |
|---|---|---|
| Details | GET /api/logs/v2/getDailyList | Line-item reconciliation, cost split |
| Day summary | GET /api/logs/v2/getDailySummary | Daily trends, monthly overviews |
| Item | Value |
|---|---|
| Base URL | https://client.tennda.ai/prod-api |
| Format / encoding | application/json · UTF-8 |
| Protocol | HTTPS |
| Currency / timezone | USD · utc+8 |
Authentication
Send a Bearer credential in the request header — either option works:Authorization: Bearer <access_token>
Authorization: Bearer sk-xxxxxxxxxxxxxxxxxxxx
| Credential | Notes |
|---|---|
| System access token | Console → Profile → Access token |
| API Key | Usually starts with sk- |
401, business code 40100.
Never put a full key in URLs, frontend code, logs, or support tickets.
Common response
{
"code": 0,
"message": "success",
"data": {
"total": 128,
"rows": []
}
}
| Field | Type | Description |
|---|---|---|
code | integer | 0 = success; non-zero = failure |
message | string | Status message |
data | object / null | Payload; null on failure |
data.total | integer | Total matching rows (for pagination) |
data.rows | array | Current page |
Business error codes
| code | HTTP | Description |
|---|---|---|
| 0 | 200 | Success |
| 40001 | 400 | Bad parameter format (invalid date, non-positive page params, etc.) |
| 40002 | 400 | Missing required parameter |
| 40003 | 400 | Invalid date range (start after end, or span over the limit) |
| 40100 | 401 | Token missing, invalid, or expired |
| 50000 | 500 | Internal server error |
Details · getDailyList
GET https://client.tennda.ai/prod-api/api/logs/v2/getDailyList
(bill day × account × model × token type).
Request parameters
string
required
Start date,
yyyy-MM-ddstring
required
End date,
yyyy-MM-dd; span with startDate must not exceed 92 daysstring
Filter by account ID. Multi-sub-account visibility requires Enterprise Management — see Conventions. Omit to return accounts visible to the current credential.
string
Filter by account name (exact). Same Enterprise Management rules; omit to return accounts visible to the current credential.
string
required
Positive integer string,
1 ~ 100; values over 100 are capped at 100string
required
Positive integer string, starting at
1Pagination is by detail row;
data.total is the total number of matching detail rows.Response fields (data.rows[])
| Field | Type | Description |
|---|---|---|
billMonth | string | Billing month, yyyyMM |
billDay | string | Billing day, yyyy-MM-dd |
billingDateTimezone | string | Always utc+8 |
account | string | Account name |
modelType | string | Model family, e.g. claude, gpt, gemini |
modelName | string | Model name |
tokenType | string | Token type — see below |
tokenCount | string | Token count for this type (integer as string) |
tokenUnit | string | Always million (priced per million tokens) |
currency | string | Always USD |
subtotalBeforeTax | string | Pre-discount, pre-tax (8 decimals) |
subtotalAfterDiscount | string | Post-discount, pre-tax (8 decimals) |
totalAmountAfterTax | string | Post-discount, post-tax (8 decimals) |
price | string | Pre-discount unit price per million tokens (8 decimals). subtotalBeforeTax = price × tokenCount ÷ 1,000,000; may be omitted when tokenCount is 0 |
entryType | string | normal = normal consumption |
tokenType
| Value | Meaning |
|---|---|
textInputTokens | Text input |
textOutputTokens | Text output |
reasoningTokens | Reasoning / thinking |
cacheCreationTokens5m | Cache creation (5-minute TTL) |
cacheCreationTokens1h | Cache creation (1-hour TTL) |
cacheTokens | Cache hit (read) |
imageInputTokens / imageOutputTokens | Image in / out |
audioInputTokens / audioOutputTokens | Audio in / out |
videoInputTokens / videoOutputTokens | Video in / out |
Sort:
billDay desc, then account, modelName, tokenType, currency asc.Request example
curl "https://client.tennda.ai/prod-api/api/logs/v2/getDailyList?startDate=2026-07-27&endDate=2026-07-27&pageSize=100&pageNum=1" \
-H "Authorization: Bearer <your_token>"
Response example
{
"code": 0,
"message": "success",
"data": {
"total": 23,
"rows": [
{
"billMonth": "202607",
"billDay": "2026-07-27",
"billingDateTimezone": "utc+8",
"account": "your_account",
"modelType": "claude",
"modelName": "claude-opus-4-8",
"tokenType": "textOutputTokens",
"tokenCount": "2931",
"tokenUnit": "million",
"currency": "USD",
"subtotalBeforeTax": "0.07327564",
"subtotalAfterDiscount": "0.07327564",
"totalAmountAfterTax": "0.07327564",
"price": "25.00021836",
"entryType": "normal"
}
]
}
}
Day summary · getDailySummary
GET https://client.tennda.ai/prod-api/api/logs/v2/getDailySummary
Request parameters
string
required
Start date,
yyyy-MM-ddstring
required
End date,
yyyy-MM-dd; span with startDate must not exceed 366 daysstring
Filter by account ID (same Enterprise Management rules as details)
string
Filter by account name, exact (same Enterprise Management rules as details)
string
required
Positive integer string,
1 ~ 400; values over 400 are capped at 400string
required
Positive integer string, starting at
1Pagination is by
(bill day × account); data.total is the combination count.Response fields (data.rows[])
| Field | Type | Description |
|---|---|---|
billMonth | string | Billing month, yyyyMM |
billDay | string | Billing day, yyyy-MM-dd |
billingDateTimezone | string | Always utc+8 |
account | string | Account name |
currency | string | Always USD |
subtotalBeforeTax | string | Day total, pre-discount pre-tax net (8 decimals) |
subtotalAfterDiscount | string | Day total, post-discount pre-tax net (8 decimals) |
totalAmountAfterTax | string | Day total, post-discount post-tax net (8 decimals) |
Sort:
billDay desc, then account asc.Request example
curl "https://client.tennda.ai/prod-api/api/logs/v2/getDailySummary?startDate=2026-07-01&endDate=2026-07-27&pageSize=31&pageNum=1" \
-H "Authorization: Bearer <your_token>"
Response example
{
"code": 0,
"message": "success",
"data": {
"total": 2,
"rows": [
{
"billMonth": "202607",
"billDay": "2026-07-27",
"billingDateTimezone": "utc+8",
"account": "your_account",
"currency": "USD",
"subtotalBeforeTax": "0.26569400",
"subtotalAfterDiscount": "0.26569400",
"totalAmountAfterTax": "0.26569400"
}
]
}
}
Conventions
- Amounts: Always
stringwith 8 decimal places — use Decimal / BigDecimal; do not convert to binary floats. - Consistency: For the same bill day and account, each day-summary amount equals the sum of the matching detail amounts that day (sum across pages).
- Zero days: Days with no consumption in the range are omitted.
- Freshness: Bills are T+1 — the latest day is queryable the next day.
- Account scope: Default is own account only. A primary account with Enterprise Management can see all sub-accounts — omit
userId/userNamefor all, or pass them to filter; a sub-account credential only sees itself. Contact a TenndaAI admin to enable Enterprise Management. - Out-of-range pages: When
pageNumexceeds total pages,rowsis empty whiletotalremains the true total.
