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

# 사용량 집계 조회

GET https://api.claw-ops.com/v1/accounts/{accountId}/usage

계정의 사용량을 **집계된 형태로** 돌려줍니다. 통화 목록을 받아 직접 합산하는 것과 다릅니다 —

- **사후 정정이 반영됩니다.** 통화 시간은 종료 후에도 정정될 수 있고(`durationOverride`,
  늦은 finalize, 운영 보정), 이 API 는 호출 시점에 원장을 다시 집계하므로 그 정정이
  항상 반영됩니다. 목록을 긁어 직접 쌓으면 이미 받아간 건의 정정이 반영되지 않아
  청구서와 계속 어긋납니다.
- **청구 주기 경계가 맞습니다.** 주기 시작은 일 경계가 아니라 가입 순간의 시각입니다.
  달력 월로 합산하면 어긋납니다.
- **통화 목록에 없는 미터도 포함됩니다.** 문자·전사·요약은 `call_logs` 에 없습니다.

## 정산에 쓰는 축

`groupBy=linkId` 가 관리번호 파트너의 정산 단위입니다. 이 값은 `assignment.completed`
webhook 의 `LinkId` 와 같아서 파트너가 자기 사용자와 바로 조인할 수 있습니다.
관리번호로 발급되지 않은 번호는 `linkId` 가 `null` 로 묶입니다.

⚠️ **번호(`groupBy=number`)를 정산 단위로 쓰지 마십시오.** 번호는 반납 후 재배정됩니다.
같은 번호에 이전 이용자의 사용량이 섞이지 않도록 이 API 는 배정 구간으로 잘라 집계하지만,
수신측에서 번호를 키로 누적하면 그 구분이 사라집니다.

## 단위 — 초로 돌려줍니다

통화·전사·요약은 **초**입니다. 청구는 계정 합계에 올림을 한 번만 겁니다
(`ceil((발신+전환)/60)`). 축을 쪼갠 값을 각각 올림해 더하면 합계가 청구서보다 커지므로,
올림한 분은 `totals[].billableMinutes` 에만 담습니다.

## 금액

`records` 에는 **수량만** 담깁니다. 단가가 누진 구간(발신 60 → 45 → 25원)이라 축별로
금액을 나눌 수 없습니다 — 합계에만 정의됩니다.

⚠️ **현재 `amount` 와 `totals[].unitPriceHint` 는 항상 `null` 입니다.** 금액 계산은 플랜
포함량·누진 구간·애드온을 함께 봐야 해서 청구 경로를 재사용하는 후속 작업으로 남았습니다.
지금은 **수량**에 자사 단가를 적용해 사용하십시오.

Reference: https://docs.claw-ops.com/api-레퍼런스/claw-ops-api/usage/get-usage

## Authentication

- `Authorization` header (bearer token, required) — API Key를 Bearer 토큰으로 전달

## Request

### Path parameters

- `accountId` (string, required) — 계정 ID

### Query parameters

- `from` (datetime, optional) — 집계 시작(UTC, 포함). `to` 와 **함께** 주어야 합니다 — 한쪽만 주면 400. 생략하면 현재 청구 주기 전체를 집계합니다.
- `to` (datetime, optional) — 집계 끝(UTC, **미포함**). 구간 최대 92일.
- `groupBy` (list of enum, optional) — 쪼갤 축. 반복 지정으로 조합합니다(`?groupBy=day&groupBy=linkId`). ⚠️ 콤마 구분(`?groupBy=day,linkId`)은 받지 않습니다 — 값 검증을 유지하기 위한 선택이라, 오타는 조용히 무시되지 않고 400 이 됩니다. 생략하면 미터별 합계만 돌려줍니다.
  - Allowed values: `meter`, `day`, `linkId`, `number`
- `meter` (list of enum, optional) — 특정 미터만 조회. 반복 지정(`?meter=a&meter=b`). 생략 시 전체.
  - Allowed values: `outbound_seconds`, `inbound_seconds`, `transfer_seconds`, `sms`, `lms`, `mms`, `ata`, `transcription_seconds`, `summary_seconds`, `amd_invocations`
- `includeToday` (boolean, optional, default: false) — 진행 중인 오늘(UTC) 구간을 포함할지. 오늘 구간은 다시 조회할 때마다 값이 달라지고 스캔 구간도 그만큼 늘어납니다. 정산은 확정 구간만 쓰는 것이 정상이므로 기본은 false 입니다.
- `pageSize` (integer, optional) — 페이지당 항목 수(기본 200, 최대 1000).
- `page_size` (integer, optional, deprecated) — `pageSize` 의 별칭(하위호환). 신규 연동은 `pageSize` 를 사용하세요.
- `limit` (integer, optional, deprecated) — `pageSize` 의 별칭(하위호환). 신규 연동은 `pageSize` 를 사용하세요.
- `page` (integer, optional, default: 1) — 1-based 페이지 번호.

## Response

### 200

집계 결과

- `meta` (object, required) — 페이징 상태. 다른 목록 API 의 `meta` 와 같은 모양이다. `total` 은 **`records` 를 쪼갠 전체 행 수**이며, 축을 지정할수록 커진다. `totals`(사용량 합계)와는 다른 것이다.
  - `page` (integer, required)
  - `pageSize` (integer, required)
  - `total` (integer, required) — 페이징 전 `records` 전체 개수. 마지막 페이지 판단에 쓴다.
- `period` (object, required) — 집계 구간. `from`/`to` 를 주지 않으면 **현재 청구 주기**가 들어간다. ⚠️ 경계는 전부 **UTC** 다. 청구 주기 시작은 일 경계가 아니라 가입 순간의 시각이 그대로 쓰인다(실측: 03:56:49, 11:21:39). 달력 월로 직접 합산하면 우리 청구서와 어긋나므로 정산에는 이 응답의 `from`/`to` 를 그대로 쓸 것.
  - `from` (datetime, required)
  - `to` (datetime, required)
  - `timezone` (enum, required) — 모든 경계와 `day` 값의 기준 시간대. 현재 UTC 고정이다.
    - Allowed values: `UTC`
  - `confirmedThrough` (date, required) — 사후 정정(지연 종료 처리·운영 보정)이 반영된 확정 구간의 마지막 날(UTC). 이후 구간은 나중에 다시 조회하면 값이 바뀔 수 있다.
  - `includesToday` (boolean, required) — 진행 중인 오늘(UTC) 구간이 포함되었는지. `includeToday=true` 일 때만 참.
- `records` (list of object, required) — `groupBy` 축으로 쪼갠 사용량. **수량만 담는다.** - 통화·전사·요약은 **초**다. 분 올림은 여기서 하지 않는다(축마다 올림하면 합계가 커진다). - 금액도 담지 않는다 — 단가가 누진 구간이라 축별로 나눌 수 없다.
  - `meter` (enum, required) — 사용량 미터. 새 미터가 늘어도 응답 스키마는 바뀌지 않는다(행이 하나 늘 뿐). ⚠️ 통화·전사·요약은 **초**로 돌려준다. 분 환산(올림)은 청구 정책이고 **계정 합계에만** 적용되므로 `totals[].billableMinutes` 에만 담긴다 — 축을 쪼갠 값을 각각 올림해 더하면 합계가 청구서보다 커진다.
    - Allowed values: `outbound_seconds`, `inbound_seconds`, `transfer_seconds`, `sms`, `lms`, `mms`, `ata`, `transcription_seconds`, `summary_seconds`, `amd_invocations`
  - `unit` (enum, required)
    - Allowed values: `second`, `count`
  - `quantity` (double, required) — 초 또는 건수. 올림하지 않은 원값이다.
  - `day` (date, optional) — `groupBy` 에 `day` 가 포함될 때만. UTC 기준.
  - `linkId` (string, optional, nullable) — '`groupBy` 에 `linkId` 가 포함될 때만. 관리번호 발급 링크 ID — `assignment.completed` webhook 의 `LinkId` 와 같은 값이라 파트너가 자기 사용자와 바로 조인할 수 있다. 관리번호로 발급되지 않은 번호(직접 발급)는 `null` 로 묶인다. ⛔ 이 값은 **발급 링크 주소에 포함되는 값과 동일**하다. 정산서·CSV 등 외부로 나가는 문서에 그대로 싣지 말 것 — 자사 고객 ID 로 치환해 쓴다.'
  - `phoneNumber` (string, optional) — '`groupBy` 에 `number` 가 포함될 때만. 저장된 형태 그대로(E.164 아님). ⚠️ 번호는 반납 후 재배정된다 — 이 API 는 배정 구간으로 잘라 집계하지만, 수신측에서 번호를 키로 누적하면 그 구분이 사라진다. 정산 키는 `linkId` 다.'
- `totals` (list of object, required) — 축과 무관한 계정 전체 합계. `records` 를 미터별로 합한 값과 일치한다.
  - `meter` (enum, required) — 사용량 미터. 새 미터가 늘어도 응답 스키마는 바뀌지 않는다(행이 하나 늘 뿐). ⚠️ 통화·전사·요약은 **초**로 돌려준다. 분 환산(올림)은 청구 정책이고 **계정 합계에만** 적용되므로 `totals[].billableMinutes` 에만 담긴다 — 축을 쪼갠 값을 각각 올림해 더하면 합계가 청구서보다 커진다.
    - Allowed values: `outbound_seconds`, `inbound_seconds`, `transfer_seconds`, `sms`, `lms`, `mms`, `ata`, `transcription_seconds`, `summary_seconds`, `amd_invocations`
  - `unit` (enum, required)
    - Allowed values: `second`, `count`
  - `quantity` (double, required)
  - `billableMinutes` (integer, optional, nullable) — 청구와 **같은 규칙**으로 올림한 분. 계정 합계에만 정의된다. - `outbound_seconds` → `ceil((발신 + 전환) / 60)`. **전환 초가 여기 합산되므로** `transfer_seconds` 행은 항상 `null` 이다. ⭐ `meter=outbound_seconds` 만 요청해도 전환은 이 계산에 포함된다(요청하지 않았다면 응답에는 나오지 않는다) — 필터에 따라 청구 분이 달라지지 않게 한다. - `inbound_seconds` → `ceil(수신 / 60)` - `transcription_seconds` · `summary_seconds` → 각각 `ceil(초 / 60)` - 건수 미터(`sms`·`lms`·`mms`·`amd_invocations`)는 `null`.
  - `unitPriceHint` (integer, optional, nullable) — ⚠️ **현재는 항상 `null` 이다**(금액 계산은 청구 경로 재사용이 필요해 후속 작업). 채워지면 아래 의미를 갖는다. 조회 시점에 이 미터가 놓인 **누진 구간의 단가(KRW, 부가세 포함)**. 참고값이다 — 사용량이 늘어 다음 구간으로 넘어가면 달라지고, 플랜 포함량 안에 있으면 `null` 이다. 이 값을 수량에 곱한 것은 청구액이 아니다.
- `amount` (object, optional, nullable) — 이번 구간의 종량 예상액. ⚠️ **현재는 어떤 조회에서도 항상 `null` 이다** — 금액 계산은 플랜 포함량·누진 구간· 애드온을 함께 봐야 해서 청구 경로를 재사용하는 후속 작업으로 남았다. 아래는 채워졌을 때의 계약이다. **다음 두 경우에는 그때에도 `null` 이다.** - `groupBy` 에 `linkId`·`number`·`day` 중 하나라도 지정한 조회 — 축별 금액은 제공하지 않는다. - `from`/`to` 로 청구 주기가 아닌 임의 구간을 조회한 경우. ⚠️ 확정 청구액이 아니다. 정기 결제 시점의 플랜료·애드온·일할 계산은 포함되지 않는다.
  - `currency` (enum, required)
    - Allowed values: `KRW`
  - `vatIncluded` (boolean, required) — 항상 `true`. 표시가는 부가세 포함이 원칙이다.
  - `meteredTotal` (integer, required) — 플랜 포함량을 넘은 종량분의 합계.
  - `breakdown` (list of object, optional) — 미터별 종량 금액. 축이 아니라 미터로만 나눈 것이라 누진 단가와 어긋나지 않는다.
    - `meter` (enum, required) — 사용량 미터. 새 미터가 늘어도 응답 스키마는 바뀌지 않는다(행이 하나 늘 뿐). ⚠️ 통화·전사·요약은 **초**로 돌려준다. 분 환산(올림)은 청구 정책이고 **계정 합계에만** 적용되므로 `totals[].billableMinutes` 에만 담긴다 — 축을 쪼갠 값을 각각 올림해 더하면 합계가 청구서보다 커진다.
      - Allowed values: `outbound_seconds`, `inbound_seconds`, `transfer_seconds`, `sms`, `lms`, `mms`, `ata`, `transcription_seconds`, `summary_seconds`, `amd_invocations`
    - `quantity` (double, required)
    - `amount` (integer, required)

## Examples

**Response**

```json
{
  "meta": {
    "page": 1,
    "pageSize": 200,
    "total": 39
  },
  "period": {
    "from": "2026-08-03T03:56:49.081Z",
    "to": "2026-09-03T03:56:49.081Z",
    "timezone": "UTC",
    "confirmedThrough": "2026-08-20",
    "includesToday": true
  },
  "records": [
    {
      "meter": "outbound_seconds",
      "unit": "second",
      "quantity": 24680,
      "day": "2023-01-15",
      "linkId": "AL7f2c9a1b",
      "phoneNumber": "07052753941"
    }
  ],
  "totals": [
    {
      "meter": "outbound_seconds",
      "unit": "second",
      "quantity": 1.1,
      "billableMinutes": 442,
      "unitPriceHint": 60
    }
  ],
  "amount": {
    "currency": "KRW",
    "vatIncluded": true,
    "meteredTotal": 24700,
    "breakdown": [
      {
        "meter": "outbound_seconds",
        "quantity": 1.1,
        "amount": 1
      }
    ]
  }
}
```

**SDK Code**

```python
import requests

url = "https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage"

querystring = {"from":"2026-08-01T00:00:00Z","groupBy":"[\"day\",\"linkId\"]","includeToday":"false","limit":"200","meter":"[\"outbound_seconds\",\"sms\"]","page":"1","pageSize":"200","page_size":"200","to":"2026-09-01T00:00:00Z"}

headers = {"Authorization": "Bearer <token>"}

response = requests.get(url, headers=headers, params=querystring)

print(response.json())
```

```javascript
const url = 'https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z';
const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};

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"
	"net/http"
	"io"
)

func main() {

	url := "https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z"

	req, _ := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", "Bearer <token>")

	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.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z")

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

request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'

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.get("https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z")
  .header("Authorization", "Bearer <token>")
  .asString();
```

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

$client = new \GuzzleHttp\Client();

$response = $client->request('GET', 'https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z', [
  'headers' => [
    'Authorization' => 'Bearer <token>',
  ],
]);

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

```csharp
using RestSharp;

var client = new RestClient("https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "Bearer <token>");
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = ["Authorization": "Bearer <token>"]

let request = NSMutableURLRequest(url: NSURL(string: "https://api.claw-ops.com/v1/accounts/AC1a2b3c4d/usage?from=2026-08-01T00%3A00%3A00Z&groupBy=%5B%22day%22%2C%22linkId%22%5D&includeToday=false&limit=200&meter=%5B%22outbound_seconds%22%2C%22sms%22%5D&page=1&pageSize=200&page_size=200&to=2026-09-01T00%3A00%3A00Z")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

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()
```