Google Health API는 표준 검색 메서드를 호출할 때 반환되는 데이터 포인트를 제한하기 위해 필터 표현식을 사용합니다. 특히 필터는 다음과 함께 사용됩니다.
list엔드포인트: 원시 데이터 포인트 쿼리reconcile엔드포인트: 조정된 스트림 데이터 포인트를 쿼리합니다.
이 가이드에서는 필터링이 구현되는 방식, 문법 및 규칙을 설명하고 각 레코드 유형의 예를 제공합니다.
필터 규칙
Google Health API는 Google API 개선 제안서 (AIP-160) 표준에 따라 필터를 구현합니다.
쿼리 매개변수
필터는 filter 매개변수의 URL 쿼리 문자열을 사용하여 전달됩니다.
필터에는 특수 문자 (예: <, >=, 따옴표)가 포함되어 있으므로 HTTP 요청에서 전송될 때 URL로 인코딩되어야 합니다.
예를 들어 steps.interval.civil_start_time >= "2026-03-04T00:00:00" 필터가 있는 요청은 다음과 같이 인코딩해야 합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints ?filter=steps.interval.civil_start_time%20%3E%3D%20%222026-03-04T00%3A00%3A00%22
이름 지정 형식
데이터 유형에 단어가 두 개 이상 있는 경우 리소스 경로와 필터 간에 식별자 형식이 다릅니다.
- 엔드포인트 경로: 하이픈을 사용합니다 (예:
/dataTypes/body-fat). - 필터 문자열: 밑줄을 사용합니다 (예:
body_fat).
필터 식별자에 하이픈을 사용하면 API에서 오류 이유가 INVALID_DATA_POINT_FILTER인 400 Bad Request을 반환합니다.
필터 유형 및 필드
필터에 사용하는 필드는 쿼리하는 데이터 유형의 레코드 유형에 따라 다릅니다. 다음 표에서는 각 레코드 유형에 지원되는 필드를 설명합니다.
| 레코드 유형 | 설명 | 지원되는 필터 필드 |
|---|---|---|
| 간격 | 일정 기간 (예: steps, distance, calories) 동안 기록된 데이터 포인트입니다. |
{dataType}.interval.start_time{dataType}.interval.civil_start_time
예: steps.interval.civil_start_time
|
| 샘플 | 순간 관찰 또는 짧은 측정 (예: weight, body-fat, blood-glucose) |
{dataType}.sample_time.physical_time{dataType}.sample_time.civil_time
예: body_fat.sample_time.physical_time
|
| 매일 | 일일 측정항목 및 요약 (예: daily-heart-rate-variability) |
{dataType}.date
예: daily_heart_rate_variability.date
|
| 세션 | 특수 타이밍 규칙이 적용된 확장 사용자 세션입니다. |
운동: {dataType}.interval.civil_start_time수면: sleep.interval.end_time, sleep.interval.civil_end_time심전도: electrocardiogram.interval.start_time
예: sleep.interval.civil_end_time
|
지원되는 연산자 및 형식 규칙
필터 표현식은 다음 형식 규칙을 준수해야 합니다.
비교 연산자
두 개의 비교 연산자만 지원됩니다.
| 연산자 | 설명 |
|---|---|
>= |
하한 (포함)입니다. |
< |
상한 (제외)입니다. |
다른 비교 연산자 (예: >, <=, =)를 사용하면 INVALID_DATA_POINT_FILTER_RESTRICTION_COMPARATOR이 있는 400 Bad Request이 반환됩니다.
논리 연산자
| 연산자 | 지원 |
|---|---|
AND |
기본값이며 유일하게 지원되는 연산자입니다. 시작 및 종료 경계를 결합하는 데 사용됩니다. |
OR |
지원되지 않음 OR를 사용하면 오류 이유가 INVALID_DATA_POINT_FILTER_EXPRESSION_STRUCTURE ('필터는 제한의 접속사 또는 시퀀스여야 합니다.400 Bad Request DISJUNCTION'을 찾았습니다. |
시간 리터럴
필터 표현식의 시간 리터럴은 다음 형식을 사용해야 합니다.
| 시간 유형 | 요구사항 | 예 |
|---|---|---|
| 실제 시간 | RFC 3339 형식이며 Z 또는 UTC 오프셋으로 끝납니다. UTC의 실제 이벤트를 나타냅니다. |
정답: "2026-03-01T00:00:00Z", "2026-03-01T00:00:00-08:00"오답: "2026-03-01" |
| 민간 시간 | ISO 8601 형식: YYYY-MM-DD[THH:mm:ss] 위치 또는 시간대 오프셋과 관계없이 사용자 시계의 시간을 나타냅니다. |
올바름: "2026-03-01", "2026-03-01T08:30:00" |
필터 유효성 검사 규칙
필터는 다음 규칙을 따릅니다.
| 규칙 | 설명 | 예 |
|---|---|---|
| 혼합 시간 유형 없음 | 물리적 시간 경계와 시민 시간 경계를 혼합하지 마세요. | 잘못됨: steps.interval.start_time >= "2026-03-01T00:00:00Z" AND steps.interval.civil_start_time < "2026-03-02"오류: INVALID_DATA_POINT_FILTER_MIXED_TIME_RESTRICTIONS ('필터에 실제 시간 범위와 민간 시간 범위를 모두 포함할 수 없습니다') |
| 혼합 데이터 유형 없음 | 요청 경로에서 데이터 유형에 속하는 필드를 사용합니다. | 잘못됨: 필터 distance.interval.start_time >= "..."이(가) 있는 경로 /users/.../dataTypes/steps/...오류: INVALID_DATA_POINT_FILTER_COLLECTION_MISMATCH('필터의 데이터 유형이 상위 데이터 유형 컬렉션과 일치하지 않음') |
| 시간 순서 로직 | 상한은 하한보다 커야 합니다. | 잘못됨: steps.interval.start_time >= "2026-03-02T00:00:00Z" AND steps.interval.start_time < "2026-03-01T00:00:00Z"오류: INVALID_TIME_RANGE ('쿼리 종료 시간은 시작 시간보다 커야 합니다') |
| 케이스 형식 | 데이터 유형 이름과 일치하는 스네이크 표기법을 사용합니다. | 잘못됨: body-fat.sample_time.physical_time >= "..."오류: INVALID_DATA_POINT_FILTER ('잘못된 필터') |
API는 잘못된 필터에 대해 HTTP 400 Bad Request를 반환합니다. 오류 이유 코드는 error.details[].metadata.detailedReasons를 확인하세요.
필수 OAuth 범위
필터를 사용하여 데이터 포인트를 쿼리하려면 승인된 사용자가 해당 데이터 유형에 대한 관련 OAuth 범위를 부여해야 합니다. 필수 범위가 누락되면 API는 MISSING_OAUTH_SCOPE와 함께 403 Forbidden 상태를 반환합니다.
다음 표에서는 OAuth 범위와 읽기 액세스 권한을 부여하는 데이터 유형을 매핑합니다. 표에는 범위가 상대 형식으로 표시됩니다 (예: .activity_and_fitness.readonly는 https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly를 나타냄).
| 필수 OAuth 범위 | 데이터 유형 |
|---|---|
.activity_and_fitness.readonly |
active-energy-burned, active-minutes, active-zone-minutes, activity-level, altitude, calories-in-heart-rate-zone, daily-vo2-max, distance, exercise, floors, run-vo2-max, sedentary-period, steps, swim-lengths-data, time-in-heart-rate-zone, total-calories, vo2-max |
.ecg.readonly |
심전도 |
.health_metrics_and_measurements.readonly |
blood-glucose, body-fat, core-body-temperature, daily-heart-rate-variability, daily-heart-rate-zones, daily-oxygen-saturation, daily-respiratory-rate, daily-resting-heart-rate, daily-sleep-temperature-derivations, heart-rate, heart-rate-variability, height, oxygen-saturation, respiratory-rate-sleep-summary, skin-temperature-sensors, weight |
.irn.readonly |
불규칙한 심장 리듬 알림 |
.nutrition.readonly |
food, food-measurement-unit, hydration-log, nutrition-log |
.sleep.readonly |
sleep |
요청된 데이터 유형에 대해 클라이언트에 쓰기 범위 (예: .activity_and_fitness.writeonly)만 부여된 경우에도 자체 클라이언트가 작성한 데이터 포인트를 쿼리할 수 있습니다. 이 경우 요청은 self-sources 데이터 소스 패밀리로 암시적으로 제한됩니다. 자세한 내용과 데이터 유형 제한사항은 데이터 소스 계열별로 필터링하기를 참고하세요.
사용 사례 및 예시
다음 예에서는 일반적인 시나리오에 맞게 필터를 구성하는 방법을 보여줍니다.
물리적 시간 범위로 걸음 수 쿼리
2026-03-01T00:00:00Z (포함)과 2026-03-02T00:00:00Z (제외) 사이에 기록된 걸음 수를 쿼리합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints ?filter=steps.interval.start_time >= "2026-03-01T00:00:00Z" AND steps.interval.start_time < "2026-03-02T00:00:00Z"
민간 시간 범위별 단계 쿼리
2026년 3월 4일 하루 동안의 단계를 사용자의 현지 시계에 상대적으로 쿼리합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints ?filter=steps.interval.civil_start_time >= "2026-03-04" AND steps.interval.civil_start_time < "2026-03-05"
신체 샘플 시간별 체지방 쿼리
2026년 3월부터 기록된 체지방 측정치를 쿼리합니다 (UTC).
GET https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints ?filter=body_fat.sample_time.physical_time >= "2026-03-01T00:00:00Z"
민간 샘플 시간별 쿼리 가중치
특정 현지 시간 이후에 기록된 체중 로그를 쿼리합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/weight/dataPoints ?filter=weight.sample_time.civil_time >= "2026-03-01T08:00:00"
심박 변이도 일일 요약 쿼리
특정 날짜 전에 기록된 심박 변이도 (HRV) 일일 요약을 쿼리합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/daily-heart-rate-variability/dataPoints ?filter=daily_heart_rate_variability.date < "2026-08-15"
시민 시작 시간으로 운동 세션 쿼리
사용자의 현지 시간대의 특정 주에 발생한 운동 세션을 쿼리합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints ?filter=exercise.interval.civil_start_time >= "2026-03-01" AND exercise.interval.civil_start_time < "2026-03-08"
민간 종료 시간으로 수면 세션 쿼리
세션의 종료 시간으로 수면 기록을 쿼리합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints ?filter=sleep.interval.civil_end_time >= "2026-03-04T05:00:00" AND sleep.interval.civil_end_time < "2026-03-04T12:00:00"
시작 시간으로 심전도 쿼리
특정 시점의 실제 UTC부터 심전도 기록을 쿼리합니다. ECG 쿼리는 start_time에서 >= 비교만 지원합니다.
GET https://health.googleapis.com/v4/users/me/dataTypes/electrocardiogram/dataPoints ?filter=electrocardiogram.interval.start_time >= "2026-03-10T12:00:00Z"
데이터 소스 패밀리별 필터링
데이터 소스 제품군은 데이터 소스 (예: 스마트시계, 모바일 앱, 수동 입력)의 논리적 그룹입니다. 특정 유형의 소스 (예: 물리적 웨어러블 기기, 수동 입력 또는 자체 클라이언트에서 작성한 데이터)의 데이터를 격리하거나 집계할 수 있습니다.
list, reconcile, rollUp, dailyRollUp 엔드포인트는 모두 dataSourceFamily 매개변수를 지원합니다. 전달 메커니즘은 엔드포인트에 따라 다릅니다.
| 엔드포인트 (HTTP 메서드) | 메커니즘 |
|---|---|
list (GET) |
dataSourceFamily을 URL 쿼리 매개변수로 전달합니다. |
reconcile (GET) |
dataSourceFamily을 URL 쿼리 매개변수로 전달합니다. |
rollUp (POST) |
dataSourceFamily를 JSON 요청 본문의 필드로 전달합니다. |
dailyRollUp (POST) |
dataSourceFamily를 JSON 요청 본문의 필드로 전달합니다. |
지원되는 데이터 소스 계열
다음 표에서는 지원되는 dataSourceFamily 값을 설명합니다.
| 옵션 | 설명 |
|---|---|
users/me/dataSourceFamilies/all-sources |
읽기 범위가 부여된 경우의 기본값 사용 가능한 모든 퍼스트 파티 (1P) 및 서드 파티 (3P) 데이터 소스의 데이터 포인트가 포함됩니다. 이 옵션을 사용하면 서드 파티 앱 데이터가 반환됩니다 (예: 스마트워치 걸음 수 + 서드 파티 앱 걸음 수 + 휴대전화 걸음 수 + 수동 걸음 수). |
users/me/dataSourceFamilies/google-wearables |
Google 및 Fitbit 트래커 기기 (예: Fitbit 웨어러블 트래커 및 Pixel Watch)에서 기록한 데이터가 포함됩니다. 수동으로 기록한 데이터와 휴대전화에서 추정한 데이터는 제외됩니다. 웨어러블 하드웨어에서 직접 기록한 원시 센서 원격 분석이 통합에 필요한 경우 이 옵션을 사용하세요. |
users/me/dataSourceFamilies/google-sources |
퍼스트 파티 Google 및 Fitbit 소스가 포함됩니다. 여기에는 실제 추적기 기기 기록, 헬스 커넥트의 데이터, 퍼스트 파티 앱 (예: Fitbit 앱 또는 Google 피트니스)을 통해 기록된 수동 항목이 포함됩니다. |
users/me/dataSourceFamilies/self-sources |
호출 클라이언트가 Google Health API를 통해 작성한 데이터만 포함합니다 (데이터 소스가 호출자와 동일한 OAuth 클라이언트 ID로 API를 통해 등록된 데이터 포인트). |
요청된 데이터 소스 계열과 일치하는 데이터 포인트가 없으면 오류가 아닌 빈 목록이 응답됩니다.
쓰기 전용 범위 및 자체 소스
요청된 데이터 유형에 대해 쓰기 범위 (예: .activity_and_fitness.writeonly)만 부여된 호출자는 자신이 쓴 데이터만 읽을 수 있습니다.
- 명시적인
dataSourceFamily매개변수가 없거나dataSourceFamily가users/me/dataSourceFamilies/self-sources로 설정된 요청은 암시적으로self-sources로 제한됩니다. - 쓰기 범위만 사용하여 다른 데이터 소스 패밀리 (예:
all-sources,google-wearables,google-sources)를 요청하면403 Forbidden(PERMISSION_DENIED) 오류가 발생합니다.
목록 엔드포인트의 데이터 유형 제한사항
list 엔드포인트 (users.dataTypes.dataPoints.list)에서 dataSourceFamily에 의한 필터링은 sleep, food, food-measurement-unit 데이터 유형에 지원되지 않습니다.
sleep,food또는food-measurement-unit의list에dataSourceFamily을 명시적으로 설정하면400 Bad Request(INVALID_ARGUMENT) 오류가 발생합니다.- 쓰기 범위만 부여된 경우 (호출자의 범위에 의해
self-sources에 대한 제한이 암시됨) 이러한 데이터 유형에 대해list를 호출하면403 Forbidden(PERMISSION_DENIED) 오류와 함께 실패합니다. dataSourceFamily로sleep데이터 포인트를 필터링하려면reconcile엔드포인트를 대신 사용하세요.
데이터 소스 계열 예시
자체 클라이언트가 사용자를 위해 작성한 원시 데이터 포인트를 나열하려면 dataSourceFamily 쿼리 매개변수가 users/me/dataSourceFamilies/self-sources로 설정된 list 엔드포인트를 호출합니다.
예를 들어 다음 GET 요청은 2026년 3월 1일부터 호출 클라이언트가 작성한 원시 body-fat 측정값을 나열합니다.
요청
GET https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints ?dataSourceFamily=users/me/dataSourceFamilies/self-sources &filter=body_fat.sample_time.physical_time >= "2026-03-01T00:00:00Z" Authorization: Bearer access-token Accept: application/json
응답
{
"dataPoints": [
{
"name": "users/123456789/dataTypes/body-fat/dataPoints/1234567890",
"dataSource": {
"recordingMethod": "UNKNOWN",
"application": {
"packageName": "",
"webClientId": "",
"googleWebClientId": "google-web-client-id"
},
"platform": "GOOGLE_WEB_API"
},
"bodyFat": {
"sampleTime": {
"physicalTime": "2026-03-10T10:00:00Z",
"utcOffset": "0s",
"civilTime": {
"date": {
"year": 2026,
"month": 3,
"day": 10
},
"time": {
"hours": 10
}
}
},
"percentage": 20
}
}
],
"nextPageToken": ""
}특정 데이터 소스 계열에서 조정된 데이터 스트림을 가져오려면 dataSourceFamily 쿼리 매개변수를 사용하여 reconcile 엔드포인트를 호출합니다.
예를 들어 다음 GET 요청은 2026년 3월 3일 이후의 수면을 가져옵니다.
요청
GET https://health.googleapis.com/v4/users/me/dataTypes/sleep/dataPoints:reconcile ?dataSourceFamily=users/me/dataSourceFamilies/google-wearables &filter=sleep.interval.civil_end_time >= "2026-03-03" Authorization: Bearer access-token Accept: application/json
응답
{
"dataPoints": [
{
"name": "users/2515055256096816351/dataTypes/sleep/dataPoints/2724123844716220216",
"dataSource": {
"recordingMethod": "DERIVED",
"device": {
"displayName": "Charge 6"
},
"platform": "FITBIT"
},
"sleep": {
"interval": {
"startTime": "2026-03-03T20:57:30Z",
"startUtcOffset": "0s",
"endTime": "2026-03-04T04:41:30Z",
"endUtcOffset": "0s"
},
"type": "STAGES",
"stages": [
{
"startTime": "2026-03-03T20:57:30Z",
"startUtcOffset": "0s",
"endTime": "2026-03-03T20:59:30Z",
"endUtcOffset": "0s",
"type": "AWAKE",
"createTime": "2026-03-04T04:43:40.937183Z",
"updateTime": "2026-03-04T04:43:40.937183Z"
},
{
"startTime": "2026-03-04T04:07:30Z",
"startUtcOffset": "0s",
"endTime": "2026-03-04T04:41:30Z",
"endUtcOffset": "0s",
"type": "AWAKE",
"createTime": "2026-03-04T04:43:40.937183Z",
"updateTime": "2026-03-04T04:43:40.937183Z"
}
],
"metadata": {
"stagesStatus": "SUCCEEDED",
"processed": true,
"main": true
},
"summary": {
"minutesInSleepPeriod": "464",
"minutesAfterWakeUp": "0",
"minutesToFallAsleep": "0",
"minutesAsleep": "407",
"minutesAwake": "57",
"stagesSummary": [
{
"type": "AWAKE",
"minutes": "56",
"count": "12"
},
{
"type": "LIGHT",
"minutes": "198",
"count": "19"
},
{
"type": "DEEP",
"minutes": "114",
"count": "10"
},
{
"type": "REM",
"minutes": "94",
"count": "4"
}
]
},
"createTime": "2026-03-04T04:43:40.337983Z",
"updateTime": "2026-03-04T04:43:40.937183Z"
}
}
],
"nextPageToken": ""
}특정 데이터 소스 계열로 제한된 특정 기간에 걸쳐 데이터 포인트를 집계하려면 rollUp 엔드포인트를 호출하고 JSON 요청 본문에 dataSourceFamily 필드를 전달합니다.
다음 POST 요청은 웨어러블 기기에서만 집계된 시간별 간격 (3600s)의 일중 걷기 걸음 수를 쿼리합니다.
요청
POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:rollUp
Authorization: Bearer access-token
Accept: application/json
{
"range": {
"startTime": "2026-07-29T00:00:00Z",
"endTime": "2026-07-29T23:59:59Z"
},
"windowSize": "3600s",
"dataSourceFamily": "users/me/dataSourceFamilies/google-wearables"
}응답
{
"rollupDataPoints": [
{
"startTime": "2026-07-29T08:00:00Z",
"endTime": "2026-07-29T09:00:00Z",
"steps": {
"countSum": "1200"
}
},
{
"startTime": "2026-07-29T09:00:00Z",
"endTime": "2026-07-29T10:00:00Z",
"steps": {
"countSum": "3450"
}
}
]
}특정 소스 계열의 일일 데이터 포인트를 집계하려면 dailyRollUp 엔드포인트를 호출하고 요청 본문에 dataSourceFamily 필드를 전달합니다.
예를 들어 다음 요청은 모든 서드 파티 Google 및 Fitbit 소스 (웨어러블 + 수동 입력)를 포함하여 사용자의 걸음 수에 대한 일일 롤업을 계산합니다.
요청
POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:dailyRollUp
Authorization: Bearer access-token
Accept: application/json
{
"range": {
"start": {
"date": {
"year": 2026,
"month": 7,
"day": 28
},
"time": {
"hours": 0,
"minutes": 0,
"seconds": 0,
"nanos": 0
}
},
"end": {
"date": {
"year": 2026,
"month": 7,
"day": 30
},
"time": {
"hours": 0,
"minutes": 0,
"seconds": 0,
"nanos": 0
}
}
},
"windowSizeDays": 1,
"dataSourceFamily": "users/me/dataSourceFamilies/google-sources"
}응답
{
"rollupDataPoints": [
{
"civilStartTime": {
"date": {
"year": 2026,
"month": 7,
"day": 28
},
"time": {}
},
"civilEndTime": {
"date": {
"year": 2026,
"month": 7,
"day": 28
},
"time": {
"hours": 23,
"minutes": 59,
"seconds": 59
}
},
"steps": {
"countSum": "8430"
}
},
{
"civilStartTime": {
"date": {
"year": 2026,
"month": 7,
"day": 29
},
"time": {}
},
"civilEndTime": {
"date": {
"year": 2026,
"month": 7,
"day": 29
},
"time": {
"hours": 23,
"minutes": 59,
"seconds": 59
}
},
"steps": {
"countSum": "11245"
}
}
]
}