Punkty końcowe

Na tej stronie znajdziesz omówienie konwencji interfejsu REST API oraz indeks typowych zadań interfejsu Google Health API i przykłady każdego z nich.

Konwencje API REST

Interfejs Google Health API jest zgodny ze standardami propozycji ulepszeń interfejsów API Google (AIP), a w szczególności z AIP-127 (transkodowanie HTTP i gRPC) oraz AIP-131–AIP-135 (metody standardowe). Te standardy określają, jak dane są mapowane z wiadomości protokołu na żądanie HTTP.

Parametry zapytania

Parametry zapytania są używane, gdy dane są częścią adresu URL. Dotyczy to głównie żądań GET (pobierania zasobu) lub LIST (filtrowania/stronicowania), ale jest też używane w przypadku operacji DELETE.

  • Miejsce docelowe: dołączony do adresu URL po znaku ?.
  • Składnia: pary klucz-wartość rozdzielone znakiem &.
  • Mapowanie: każde pole w wiadomości z żądaniem, które nie jest częścią szablonu ścieżki adresu URL, jest mapowane na parametr zapytania.
  • Najlepsze w przypadku: prostych typów (ciągi znaków, liczby całkowite, wyliczenia) i pól powtarzanych.

Przykładowa składnia:

GET https://health.googleapis.com/v4/users/me/dataTypes/data-type/dataPoints?page_size=10&filter=data_type.interval.start_time >= "2025-10-01T00:00:00Z"

Treść żądania

Treść żądania jest używana, gdy dane modyfikują stan zasobu lub są zbyt duże, aby można je było umieścić w adresie URL. Treść jest zwykle reprezentacją JSON samego zasobu. Zwykle używane w przypadku operacji POST, PATCH i PUT.

  • Umiejscowienie: w ładunku HTTP (niewidoczne w adresie URL).
  • Składnia: sformatowana jako obiekt JSON.
  • Mapowanie: zdefiniowane w adnotacji google.api.http.
    • body: "*" oznacza, że cała wiadomość jest treścią.
    • body: "resource_name" oznacza, że tylko określone pole w protokole jest treścią.
  • Najlepsze rozwiązanie w przypadku: złożonych obiektów, zagnieżdżonych wiadomości i danych wrażliwych.

Przykładowa składnia:

POST https://health.googleapis.com/v4/users/me/dataTypes/data-type/dataPoints:rollUp
Content-Type: application/json

{
  "range": {
    "startTime": "2025-11-05T00:00:00Z",
    "endTime": "2025-11-13T00:00:00Z"
  },
  "windowSize": "3600s"
}

Przypadek hybrydowy

W metodzie zgodnej z AIP-134 Update lub w operacji PATCH używane są obie te wartości. Adres URL zawiera nazwę zasobu, treść zawiera zaktualizowane dane zasobu, a parametr zapytania (zwykle update_mask) określa, które pola mają zostać zmienione.

PATCH https://health.googleapis.com/v4/projects/project-id/subscribers/subscriber-id
Content-Type: application/json

{
  "endpointUri": "https://myapp.com/new-webhooks/health"
}

Najważniejsze różnice w skrócie

Funkcja Parametry zapytania Treść żądania
Wskazówki dotyczące AIP Używane do wyszukiwania, filtrowania i operacji odczytu. Używany w operacjach zapisu.
Widoczność Widoczne w historii przeglądarki i logach serwera. Ukryte w adresie URL.
Złożoność Ograniczone do płaskich lub powtarzających się struktur. Obsługuje głęboko zagnieżdżone obiekty JSON.
Kodowanie Musi być zakodowany (np. spacje stają się %20). Standardowe kodowanie JSON.

Daty

Wszystkie daty w interfejsie Google Health API są wyświetlane w formacie YYYY-MM-DD. Interfejs Nutrition API obsługuje standard ISO-8601 w przypadku wartości daty pod tymi warunkami:

  • Rok w formacie 4-cyfrowym.YYYY
  • Wartości roku w zakresie 0000–9999
  • Brak egzekwowania ograniczeń daty rozpoczęcia wynikających z normy ISO-8601 lub innej epoki

Nagłówki

Wykonanie punktów końcowych interfejsu API Google Health wymaga użycia odpowiednich nagłówków i tokena dostępu. W przypadku żądań GET i POST zalecamy używanie tego nagłówka:

Authorization: Bearer access-token
Accept: application/json

Indeks zadań interfejsu API

W tej sekcji znajdziesz indeks typowych zadań interfejsu Google Health API i przykłady każdego z nich.

Uzyskiwanie identyfikatora użytkownika Fitbita lub Google

Po wyrażeniu przez użytkownika zgody za pomocą Google OAuth 2.0 odpowiedź tokenu nie zawiera identyfikatora użytkownika Fitbita ani Google. Aby uzyskać identyfikator użytkownika, wywołaj getIdentity punkt końcowy. getIdentity zwraca zarówno starszy identyfikator użytkownika Fitbita, jak i identyfikator użytkownika Google.

Zalecamy, aby od razu po wyrażeniu zgody przez nowego użytkownika za pomocą protokołu OAuth wywołać punkt końcowy getIdentity i zapisać oba identyfikatory użytkownika. Zapewnia to zgodność wsteczną i w przód w integracji.

Na przykład:

Żądanie

GET https://health.googleapis.com/v4/users/me/identity
Authorization: Bearer access-token
Accept: application/json

Odpowiedź

{
  "name": "users/me/identity",
  "legacyUserId": "A1B2C3",
  "healthUserId": "111111256096816351"
}

Uzyskiwanie danych w ciągu dnia lub szczegółowych danych zebranych w ciągu dnia

Użyj list punktu końcowego dla konkretnego typu danych, aby uzyskać dane śróddzienne lub szczegółowe zebrane w ciągu dnia w obsługiwanych przedziałach czasu dla tego typu danych.

Na przykład:

Żądanie

GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints
Authorization: Bearer access-token
Accept: application/json

Odpowiedź

{
  "dataPoints": [
    {
      "dataSource": {
        "recordingMethod": "PASSIVELY_MEASURED",
        "device": {
          "manufacturer": "",
          "displayName": "Charge 6"
        },
        "platform": "FITBIT"
      },
      "steps": {
        "interval": {
          "startTime": "2026-03-04T07:05:00Z",
          "startUtcOffset": "0s",
          "endTime": "2026-03-04T07:06:00Z",
          "endUtcOffset": "0s",
          "civilStartTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 5
            }
          },
          "civilEndTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 6
            }
          }
        },
        "count": "40"
      }
    },
...
  ],
  "nextPageToken": "Xm5h-6L0viZxIlRuWjx5bmvy98zj85uG34tuMn16mu2pntsnZI32iqhq"
}

Uzyskiwanie uzgodnionego widoku danych interwałowych

Aby pobrać dane interwałów bez nakładających się rekordów lub konfliktów na wielu urządzeniach, wywołaj reconcilepunkt końcowy. Punkt końcowy reconcile automatycznie usuwa duplikaty nakładających się przedziałów w różnych partiach synchronizacji i na różnych urządzeniach rejestrujących, zwracając autorytatywny, ciągły strumień danych, który można wykorzystać do renderowania osi czasu aktywności i obliczania czasu trwania.

Więcej informacji o tym, dlaczego połączone urządzenia generują nakładające się przedziały, oraz porównanie działania list i reconcile znajdziesz w przewodniku po zarządzaniu danymi.

Poniższy przykład porównuje odpowiedź list (która zwraca oba nakładające się rekordy) z odpowiedzią reconcile (która rozwiązuje konflikt, zwracając rekord autorytatywny) w przypadku użytkownika z 2 nakładającymi się sesjami ćwiczeń:

Lista nieprzetworzona

GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints
Authorization: Bearer access-token
Accept: application/json
{
  "dataPoints": [
    {
      "name": "users/111111256096816351/dataTypes/exercise/dataPoints/7797422996486764704",
      "exercise": {
        "interval": {
          "startTime": "2026-09-03T11:20:00Z",
          "endTime": "2026-09-03T11:50:00Z"
        },
        "exerciseType": "RUNNING"
      }
    },
    {
      "name": "users/111111256096816351/dataTypes/exercise/dataPoints/4389052750481144696",
      "exercise": {
        "interval": {
          "startTime": "2026-09-03T11:00:00Z",
          "endTime": "2026-09-03T11:30:00Z"
        },
        "exerciseType": "RUNNING"
      }
    }
  ]
}

Uzgodniono

GET https://health.googleapis.com/v4/users/me/dataTypes/exercise/dataPoints:reconcile
Authorization: Bearer access-token
Accept: application/json
{
  "dataPoints": [
    {
      "dataPointName": "users/111111256096816351/dataTypes/exercise/dataPoints/7797422996486764704",
      "exercise": {
        "interval": {
          "startTime": "2026-09-03T11:20:00Z",
          "endTime": "2026-09-03T11:50:00Z"
        },
        "exerciseType": "RUNNING"
      }
    }
  ]
}

Proces uzgadniania rozwiązuje konflikty między sesjami przez usuwanie duplikatów i wybieranie wiarygodnego rekordu zamiast tworzenia sztucznego przedziału czasu (np. od 11:00:00Z do 11:50:00Z). Uzgodniona odpowiedź zwraca zwycięski punkt danych (7797422996486764704) z jego pierwotnym zarejestrowanym przedziałem (od 11:20:00Z do 11:50:00Z), zachowując integralność pomiarów telemetrycznych i danych sesji.

Filtruj dane

Aby pobrać określone podzbiory rekordów punktów danych spełniające kryteria, takie jak przedział czasu, data lub czas obserwacji, użyj punktu końcowego list lub reconcile z parametrem filter.

Szczegółowe wytyczne, reguły formatowania, błędy weryfikacji i przykłady zapytań znajdziesz w przewodniku po filtrowaniu danych.

Filtrowanie według rodziny źródeł danych

Aby wyodrębnić lub zagregować dane z określonych typów źródeł (np. z fizycznych urządzeń do noszenia a wpisów ręcznych), użyj parametru dataSourceFamily.

Szczegółowe wytyczne, obsługiwane rodziny oraz przykłady żądań i odpowiedzi dotyczące parametrów reconcile, rollUp i dailyRollUp znajdziesz w sekcji Filtrowanie według rodziny źródeł danych w przewodniku po filtrowaniu danych.

Filtrowanie danych według czasu rozpoczęcia interwału

Użyj punktu końcowego list z parametrem filter, aby filtrować dane według czasu cywilnego lub przedziału.

.

Na przykład:

Żądanie

GET https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints?filter=steps.interval.civil_start_time >= "2026-03-04T00:00:00"
Authorization: Bearer access-token
Accept: application/json

Odpowiedź

{
  "dataPoints": [
    {
      "dataSource": {
        "recordingMethod": "PASSIVELY_MEASURED",
        "device": {
          "manufacturer": "",
          "displayName": "Charge 6"
        },
        "platform": "FITBIT"
      },
      "steps": {
        "interval": {
          "startTime": "2026-03-04T07:05:00Z",
          "startUtcOffset": "0s",
          "endTime": "2026-03-04T07:06:00Z",
          "endUtcOffset": "0s",
          "civilStartTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 5
            }
          },
          "civilEndTime": {
            "date": {
              "year": 2026,
              "month": 3,
              "day": 4
            },
            "time": {
              "hours": 7,
              "minutes": 6
            }
          }
        },
        "count": "40"
      }
...
  ],
  "nextPageToken": "Xm5h-6L0viZxIlRuQjp5bml1bZ4ve2dhNmZvMnt4Yn7qIGQhbHN3YQ"
}

Filtrowanie danych według czasu fizycznego obserwacji próbki

Użyj punktu końcowego list z parametrem filter, aby filtrować dane według czasu fizycznego obserwacji próbki.

.

Na przykład:

Żądanie

GET https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints?filter=body_fat.sample_time.physical_time >= "2026-03-01T00:00:00Z"
Authorization: Bearer access-token
Accept: application/json

Odpowiedź

{
  "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": ""
}

Filtrowanie i agregowanie według rodziny źródeł danych

Rodzina źródeł danych to logiczne zgrupowanie źródeł danych (takich jak zegarki, aplikacje mobilne czy wpisy ręczne). Umożliwia to wyodrębnianie lub agregowanie danych z określonych typów źródeł (np. z urządzeń do noszenia a z danych wprowadzanych ręcznie).

Punkty końcowe reconcile, rollUp i dailyRollUp obsługują parametr dataSourceFamily. Mechanizm przekazywania zależy od punktu końcowego:

Punkt końcowy (metoda HTTP) Mechanizm
reconcile (GET) Przekaż dataSourceFamily jako parametr zapytania w adresie URL.
rollUp (POST) Przekaż dataSourceFamily jako pole w treści żądania w formacie JSON.
dailyRollUp (POST) Przekaż dataSourceFamily jako pole w treści żądania w formacie JSON.

Obsługiwane rodziny źródeł danych

W tabeli poniżej znajdziesz opis obsługiwanych wartości dataSourceFamily:

Opcja Opis
users/me/dataSourceFamilies/all-sources Wartość domyślna. Zwraca punkty danych uzgodnione we wszystkich zarejestrowanych źródłach danych własnych i pochodzących od innych firm. W przypadku tej opcji zwracane są dane aplikacji innych firm (np. kroki z zegarka, kroki z aplikacji innych firm, kroki z telefonu komórkowego i kroki wprowadzone ręcznie).
users/me/dataSourceFamilies/google-wearables Obejmuje dane rejestrowane przez urządzenia śledzące Google i Fitbit (takie jak trackery Fitbit i zegarki Pixel Watch). Wyklucza dane rejestrowane ręcznie i dane szacowane przez telefon. Użyj tej opcji, jeśli integracja wymaga surowych danych telemetrycznych z czujników rejestrowanych bezpośrednio przez urządzenie do noszenia.
users/me/dataSourceFamilies/google-sources Obejmuje źródła własne Google i Fitbita. Obejmuje to dane z urządzeń śledzących aktywność, dane z Health Connect i wszystkie dane wprowadzone ręcznie w aplikacjach własnych (takich jak aplikacja Fitbit czy Google Fit).

.

Aby uzyskać uzgodniony strumień danych z określonej rodziny źródeł danych, wywołaj punkt końcowy reconcile z parametrem zapytania dataSourceFamily.

Na przykład to żądanie GET pobiera dane o śnie zarejestrowane przez tracker w dniu po 3 marca 2026 r.:

Żądanie

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

Odpowiedź

{
  "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": ""
}

Aby agregować punkty danych w określonym rozmiarze okna ograniczonym do konkretnej rodziny źródeł danych, wywołaj punkt końcowy rollUp i przekaż pole dataSourceFamily w treści żądania JSON.

Poniższe żądanie POST wysyła zapytanie o liczbę kroków z dnia w interwałach godzinowych (3600s), zagregowaną wyłącznie na podstawie danych z urządzeń do noszenia:

Żądanie

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"
}

Odpowiedź

{
  "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"
      }
    }
  ]
}

Aby agregować dzienne punkty danych dla konkretnej rodziny źródeł, wywołaj punkt końcowy dailyRollUp i przekaż pole dataSourceFamily w treści żądania.

Na przykład to żądanie oblicza dzienne podsumowania kroków użytkownika, w tym wszystkie źródła własne Google i Fitbita (urządzenia do noszenia i wpisy ręczne):

Żądanie

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"
}

Odpowiedź

{
  "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"
      }
    }
  ]
}

Agregowanie punktów danych w określonym przedziale czasu

Użyj rollUppunktu końcowego, aby zwrócić zagregowane punkty danych na podstawie okna w sekundach w zakresie datetime na podstawie czasu fizycznego użytkowników (w UTC).

Podczas wywoływania punktu końcowego rollUp podaj treść żądania reprezentującą wymagany zakres czasowy i windowSize. Pamiętaj o tych wymaganiach dotyczących windowSize:

  • Minimalny rozmiar okna: czas trwania windowSize musi wynosić co najmniej 1 sekundę ("1s"). Czas trwania krótszy niż sekunda, zero lub ujemny zostanie odrzucony z kodem błędu 400 Bad Request (INVALID_ROLLUP_WINDOW).
  • Dopasowanie rozdzielczości pamięci: aby uniknąć nierównomiernego rozkładu zagregowanych danych w podzakresach, wybierz wartość windowSize, która jest równa lub większa od rozdzielczości pamięci typu danych (np. "60s" w przypadku 1-minutowych przedziałów). Więcej informacji znajdziesz w artykule Rozmiar okna zbiorczego i rozdzielczość pamięci bazowej.

Jeśli na przykład chcesz zliczać kroki w 1-minutowych odstępach (60s):

Żądanie

POST https://health.googleapis.com/v4/users/me/dataTypes/steps/dataPoints:rollUp
Authorization: Bearer access-token
Accept: application/json

{
  "range": {
    "startTime": "2026-02-17T17:00:00Z",
    "endTime": "2026-02-17T17:59:59Z"
  },
  "windowSize": "60s"
}

Odpowiedź

{
  "rollupDataPoints": [
    {
      "startTime": "2026-02-17T17:55:00Z",
      "endTime": "2026-02-17T17:56:00Z",
      "steps": {
        "countSum": "72"
      }
    },
    {
      "startTime": "2026-02-17T17:54:00Z",
      "endTime": "2026-02-17T17:55:00Z",
      "steps": {
        "countSum": "85"
      }
    },
...
  ]
}

agregować dane z 1 dnia lub wielu dni;

dailyRollUpPunktu końcowego należy używać, gdy chcesz agregować dane z jednego lub wielu dni, czyli windowSize. W treści żądania podaj zakres czasu cywilnego zamknięty-otwarty dla wymaganego przedziału. W zależności od typu danych otrzymasz sumę lub średnią z przedziału czasu.

Na przykład:

Żądanie

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": 2,
        "day": 26
      },
      "time": {
        "hours": 0,
        "minutes": 0,
        "seconds": 0,
        "nanos": 0
      }
    },
    "end": {
      "date": {
        "year": 2026,
        "month": 2,
        "day": 26
      },
      "time": {
        "hours": 23,
        "minutes": 59,
        "seconds": 59,
        "nanos": 0
      }
    }
  },
  "windowSizeDays": 1
}

Odpowiedź

{
  "rollupDataPoints": [
    {
      "civilStartTime": {
        "date": {
          "year": 2026,
          "month": 2,
          "day": 26
        },
        "time": {}
      },
      "civilEndTime": {
        "date": {
          "year": 2026,
          "month": 2,
          "day": 26
        },
        "time": {
          "hours": 23,
          "minutes": 59,
          "seconds": 59
        }
      },
      "steps": {
        "countSum": "3822"
      }
    }
  ]
}

Podział na przedziały, gdy zakres nie jest wielokrotnością rozmiaru okna

Jeśli żądany zakres nie jest dokładną wielokrotnością wartości windowSize (lub windowSizeDays), ostatni przedział czasowy zostanie chronologicznie skrócony do górnego punktu końcowego zakresu i będzie obejmować okres krótszy niż rozmiar okna. Interfejs API akceptuje Twoje żądanie bez modyfikacji i nie wykonuje żadnego zaokrąglania, przesunięcia czasowego ani interpolacji danych.

Aby objąć cały żądany zakres, interfejs API używa dzielenia w górę do obliczenia łącznej liczby okien agregacji:

Number of windows = ceiling(Range duration / Window size)

Każdy przedział zaczyna się kolejno od początku zakresu. Jeśli dodanie kolejnego pełnowymiarowego okna spowoduje przekroczenie żądanego czasu zakończenia, ostatnie okno zostanie skrócone (ograniczone) do czasu zakończenia zakresu.

Jak działa podział na grupy

Gdy wysyłasz żądanie dotyczące podsumowań z zakresami niepodzielnymi, interfejs API stosuje te reguły:

  • Kategoryzowanie w przedziałach rozpoczyna się na początku wybranego zakresu (range.startTime lub range.start) i przebiega dalej o rozmiar przedziału (windowSize lub windowSizeDays).
  • Ostatni przedział chronologiczny jest ograniczony na końcu żądanego zakresu (range.endTime lub range.end), co oznacza, że obejmuje krótszy okres niż żądany rozmiar okna.
  • Zwrócone obiekty RollupDataPoint lub DailyRollupDataPoint zawierają własne sygnatury czasowe rozpoczęcia i zakończenia, których możesz użyć do sprawdzenia rzeczywistego czasu trwania obciętego zasobnika.
  • Interfejs API zwraca dane zbiorcze w odwrotnej kolejności chronologicznej (najnowsze jako pierwsze), więc ostatni przedział chronologiczny (czyli obcięty) pojawia się jako pierwszy element (index 0) na zwróconej liście.

Scenariusz: zakres 12-minutowy z 5-minutowym oknem

Załóżmy, że klient prosi o zbiorcze dane z 12-minutowego zakresu z 5-minutowymwindowSize:

  • range.startTime: 10:00:00
  • range.endTime: 10:12:00 (łączny czas trwania: 12 minut)
  • windowSize: 5 minutes

12 minut nie jest wielokrotnością 5 minut (12 = 5 * 2 + 2), więc interfejs API akceptuje żądanie i oblicza liczbę przedziałów jako ceiling(12 / 5) = 3.

W ten sposób powstaną 3 przedziały czasowe:

  1. Kosz 1: [10:00:00, 10:05:00) – czas trwania: 5 minut (pełne okno)
  2. Koszyk 2: [10:05:00, 10:10:00) – czas trwania: 5 minut (pełne okno)
  3. Kosz 3 (obcięty): [10:10:00, 10:12:00) – czas trwania: 2 minuty (obcięty na poziomie range.endTime)

Wpływ na wartości zbiorcze

Ponieważ ostatnie okno ma krótszy czas trwania, w przypadku wskaźników addytywnych (takich jak suma lub liczba kroków) wartość w obciętym przedziale będzie niższa wyłącznie ze względu na krótszy czas śledzenia.

Jeśli użytkownik przez cały 12-minutowy okres chodzi w stałym tempie 100 kroków na minutę:

  • Kosz 1 (10:00–10:05): 500 kroków (5 minut × 100 kroków/minutę)
  • Przedział 2 (10:05–10:10): 500 kroków (5 minut × 100 kroków/minutę)
  • Kosz 3 (10:10–10:12): 200 kroków (2 minuty × 100 kroków/minutę)

Przykładowa odpowiedź interfejsu API pokazująca kolejność

Interfejs API zwraca wyniki w odwrotnej kolejności chronologicznej, więc obcięty zasobnik pojawia się jako pierwszy element na liście zwróconej przez interfejs:

{
  "rollupDataPoints": [
    {
      "startTime": "2026-08-20T10:10:00Z",
      "endTime": "2026-08-20T10:12:00Z",
      "steps": {
        "countSum": "200"
      }
    },
    {
      "startTime": "2026-08-20T10:05:00Z",
      "endTime": "2026-08-20T10:10:00Z",
      "steps": {
        "countSum": "500"
      }
    },
    {
      "startTime": "2026-08-20T10:00:00Z",
      "endTime": "2026-08-20T10:05:00Z",
      "steps": {
        "countSum": "500"
      }
    }
  ]
}

Rozmiar okna zbiorczego i rozdzielczość pamięci bazowej

Punkt końcowy rollUp akceptuje dowolny windowSize o wartości co najmniej 1 sekundy, ale różne typy danych rejestrują i przechowują pomiary z różnymi częstotliwościami próbkowania lub w różnych przedziałach czasu w pamięci bazowej. Na przykład dane dotyczące aktywności fizycznej rejestrowane przez urządzenia do noszenia, takie jak steps, distance, active-minutes i active-energy-burned, są zwykle zapisywane w 1-minutowych (60s) odstępach czasu.

Podczas agregowania typów danych interwałów punkt końcowy rollUp umieszcza każdy zarejestrowany punkt danych w zasobniku zawierającym startTime tego punktu. Interfejs API nie dzieli, nie interpoluje ani nie rozdziela danych interwałowych na zasobniki podinterwałów.

Jeśli określisz windowSize mniejszy niż interwał przechowywania danych bazowych (np. zażądasz 10-sekundowego okna dla steps przechowywanych w interwałach 1-minutowych):

  1. Pierwszy podprzedział pasujący do przedziału startTime (np. 10:00:00–10:00:10) otrzymuje całą skumulowaną liczbę z minuty (np. wszystkie 100 kroków zarejestrowanych w tej minucie).
  2. Pozostałe podzasobniki w tej samej minucie (10:00:10–10:00:20, 10:00:20–10:00:30 itd.) nie otrzymują punktów danych, ponieważ w tych przedziałach nie rozpoczyna się żaden interwał.

W rezultacie otrzymujemy „skokowe” dane, w których wartość całego przedziału jest skoncentrowana w pierwszym podoknie.

Aby uzyskać równomiernie rozłożone i znaczące wartości zagregowane, zawsze ustawiaj wartość windowSize na czas trwania równy lub większy niż rozdzielczość pamięci bazowej docelowego typu danych (np. 60s lub większą w przypadku steps). Rozdzielczość pamięci i minimalne zalecane okno agregacji dla każdego typu danych znajdziesz w dokumentacji Typy danych interfejsu Google Health API.

Aktualizowanie danych dotyczących zdrowia użytkownika

Użyj patchpunktu końcowego, aby zaktualizować dane dotyczące zdrowia użytkownika.

Punkt końcowy patch aktualizuje istniejący rekord na podstawie identyfikatora podanego w adresie URL żądania. Podaj identyfikator wcześniej wstawionego punktu danych. Interfejs API zastępuje istniejący rekord.

Znaczniki czasu interwału punktu danych (startTime i endTime) mogą być też aktualizowane przez właściciela rekordu lub propagowane z platform nadrzędnych, takich jak Health Connect. Więcej informacji o możliwości zmiany sygnatur czasowych znajdziesz w przewodniku po zarządzaniu danymi. Przykład aktualizowania sygnatur czasowych interwałów znajdziesz w artykule Aktualizowanie sygnatur czasowych interwałów w przypadku istniejących danych.

Kiedy używać identyfikatora punktu danych

Identyfikator punktu danych jest niezbędny w tych sytuacjach:

  • Ukierunkowane aktualizacje: aby zaktualizować konkretny pomiar, podaj jego identyfikator w żądaniu patch.
  • Usuwanie: zachowanie identyfikatora umożliwia aplikacji późniejsze usunięcie rekordu za pomocą batchDelete punktu końcowego.

Oto przykład, w którym użytkownik aktualizuje odczyt tkanki tłuszczowej na wadze o nazwie „HumanScale” firmy „Scales R Us”. Nowy odczyt tkanki tłuszczowej użytkownika wynosi 20% w dniu 10 marca 2026 r.:

Żądanie

PATCH https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints/1234567890
Authorization: Bearer access-token
Content-Type: application/json

{
  "name": "users/me/dataTypes/body-fat/dataPoints/1234567890",
  "dataSource": {
    "recordingMethod": "ACTIVELY_MEASURED",
    "device": {
      "formFactor": "SCALE",
      "manufacturer": "Scales R Us",
      "displayName": "HumanScale"
    }
  },
  "bodyFat": {
    "sampleTime": {
      "physicalTime": "2026-03-10T10:00:00Z"
    },
    "percentage": 20
  }
}

Odpowiedź

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4main.DataPoint",
    "name": "users/123456789/dataTypes/body-fat/dataPoints/1234567890",
    "dataSource": {
      "recordingMethod": "ACTIVELY_MEASURED",
      "device": {
        "formFactor": "SCALE",
        "manufacturer": "Scales R Us",
        "displayName": "HumanScale"
      },
      "application": {
        "googleWebClientId": "618308034039.apps.googleusercontent.com"
      },
      "platform": "GOOGLE_WEB_API"
    },
    "bodyFat": {
      "sampleTime": {
        "physicalTime": "2026-03-10T10:00:00Z"
      },
      "percentage": 20
    }
  }
}

Aktualizowanie sygnatur czasowych interwałów w przypadku dotychczasowych danych

Aby zaktualizować sygnatury czasowe przedziału (startTime i endTime w ładunkach JSON REST lub start_time i end_time w gRPC) istniejącego punktu danych, wyślij żądanie PATCH do identyfikatora URI zasobu punktu danych. Pola rekordu może modyfikować tylko jego pierwotny twórca lub właściciel. Aplikacje nie mogą edytować punktów danych, których nie utworzyły.

Więcej informacji o tym, czy sygnatury czasowe mogą ulegać zmianom, o aktualizacjach z Health Connect i o implikacjach związanych z pamięcią podręczną znajdziesz w przewodniku po zarządzaniu danymi.

Poniższy przykład pokazuje, jak aplikacja właściciela aktualizuje sygnatury czasowe interwału istniejącego dziennika nawodnienia za pomocą punktu końcowego patch:

Żądanie

PATCH https://health.googleapis.com/v4/users/me/dataTypes/hydration-log/dataPoints/4093039283164890826
Authorization: Bearer access-token
Content-Type: application/json

{
  "hydrationLog": {
    "interval": {
      "startTime": "2026-09-03T10:05:00Z",
      "endTime": "2026-09-03T10:19:59Z"
    },
    "amountConsumed": {
      "milliliters": 350
    }
  }
}

Odpowiedź

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4.DataPoint",
    "name": "users/111111256096816351/dataTypes/hydration-log/dataPoints/4093039283164890826",
    "hydrationLog": {
      "interval": {
        "startTime": "2026-09-03T10:05:00Z",
        "endTime": "2026-09-03T10:19:59Z",
        "civilStartTime": {
          "date": {
            "year": 2026,
            "month": 9,
            "day": 3
          },
          "time": {
            "hours": 10,
            "minutes": 5
          }
        },
        "civilEndTime": {
          "date": {
            "year": 2026,
            "month": 9,
            "day": 3
          },
          "time": {
            "hours": 10,
            "minutes": 19,
            "seconds": 59
          }
        }
      },
      "amountConsumed": {
        "milliliters": 350
      }
    }
  }
}

Zapisywanie produktu spożywczego

Aby zarejestrować produkt spożywczy, wyślij żądanie POST do punktu końcowego nutrition-log dataPoints. Treść żądania zawiera obiekt DataPoint z obiektem nutritionLog. Więcej informacji znajdziesz w przewodniku po odżywianiu.

Na przykład:

Żądanie

POST https://health.googleapis.com/v4/users/me/dataTypes/nutrition-log/dataPoints
Authorization: Bearer access-token
Content-Type: application/json

{
  "nutritionLog": {
    "interval": {
      "startTime": "2026-06-16T12:00:00Z",
      "endTime": "2026-06-16T12:30:00Z"
    },
    "foodDisplayName": "Banana",
    "mealType": "LUNCH",
    "energy": {
      "kcal": 105
    },
    "totalCarbohydrate": {
      "grams": 27
    },
    "totalFat": {
      "grams": 0.3
    }
  }
}

Odpowiedź

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4.DataPoint",
    "name": "users/123456789/dataTypes/nutrition-log/dataPoints/567890",
    "dataSource": {
      "recordingMethod": "ACTIVELY_MEASURED",
      "platform": "GOOGLE_WEB_API"
    },
    "nutritionLog": {
      "interval": {
        "startTime": "2026-06-16T12:00:00Z",
        "startUtcOffset": "0s",
        "endTime": "2026-06-16T12:30:00Z",
        "endUtcOffset": "0s"
      },
      "energy": {
        "kcal": 105
      },
      "totalCarbohydrate": {
        "grams": 27
      },
      "totalFat": {
        "grams": 0.3
      },
      "mealType": "LUNCH",
      "foodDisplayName": "Banana"
    }
  }
}

Usuwanie danych dotyczących zdrowia użytkownika

Użyj batchDeletemetody, aby usunąć tablicę danych użytkownika z aplikacji Fitbit.

Oto przykład sytuacji, w której użytkownik wcześniej zarejestrował pomiar tkanki tłuszczowej na wadze, ale chce usunąć ten zapis. Użyj user-id i data-point-id z pierwotnego działania wstawiania:

Żądanie

POST https://health.googleapis.com/v4/users/me/dataTypes/body-fat/dataPoints:batchDelete
Authorization: Bearer access-token
Accept: application/json
content-length: 93

{
  "names": [
    "users/123456789/dataTypes/body-fat/dataPoints/1234567890"
  ]
}

Odpowiedź

{
  "done": true,
  "response": {
    "@type": "type.googleapis.com/google.devicesandservices.health.v4main.BatchDeleteDataPointsResponse"
  }
}

Znajdowanie informacji o urządzeniu

Użyj list punktu końcowego, aby pobrać listę urządzeń sparowanych z kontem użytkownika. Obejmuje to informacje o modelu urządzenia (deviceVersion) i ostatniej synchronizacji z aplikacją mobilną Google Health (lastSyncTime).

Konfiguracja listy i informacje o synchronizacji są przydatne do rozwiązywania problemów z synchronizacją lub pobierania danych historycznych od czasu ostatniej synchronizacji.

Na przykład:

Żądanie

GET https://health.googleapis.com/v4/users/me/pairedDevices
Authorization: Bearer access-token
Accept: application/json

Odpowiedź

{
  "pairedDevices": [
    {
      "name": "users/me/pairedDevices/123456",
      "deviceType": "TRACKER",
      "batteryStatus": "High",
      "batteryLevel": 88,
      "lastSyncTime": "2026-03-04T07:05:00Z",
      "deviceVersion": "Charge 6",
      "macAddress": "00:11:22:33:44:55",
      "features": [
        "STEPS",
        "HEART_RATE"
      ]
    }
  ]
}

Wykonywanie zapytań dotyczących danych historycznych

Jedną z głównych korzyści interfejsu Google Health API jest możliwość śledzenia wyników użytkownika i monitorowania jego parametrów życiowych przez długi czas. Możesz wysyłać zapytania o dane użytkownika z dowolnego okresu, w którym były one rejestrowane. Interfejs API nie nakłada żadnych ograniczeń na ilość danych historycznych, które może wykorzystywać Twoja aplikacja.

Wykonywanie zapytań dotyczących danych historycznych podlega jednak standardowym limitom liczby zapytań. Aby zarządzać stabilnością systemu i zapobiegać nadmiernym rozmiarom pakietów danych, interfejs Google Health API korzysta z automatycznego stronicowania z rozmiarami stron dostosowanymi do konkretnych punktów końcowych. Pamiętaj o tych ograniczeniach i zasadach działania:

  • Automatyczny podział na strony: jeśli zapytanie dotyczy długiego okresu danych, interfejs API zwróci tylko pierwszą stronę wyników do limitu rozmiaru strony dla danego punktu końcowego wraz z parametrem nextPageToken. Aby poprosić o kolejne strony, musisz użyć parametru nextPageToken.
  • Zmienne rozmiary stron: limity ograniczające zależą od punktu końcowego i typu danych. W przypadku większości typów danych rozmiar strony jest ograniczony do maksymalnie 10 tys. W przypadku niektórych typów danych, takich jak exercise i sleep, domyślny i maksymalny rozmiar strony jest ograniczony do 25. Jeśli na przykład klient poprosi o wszystkie dane o śnie z ostatnich 10 lat, interfejs API zwróci na pierwszej stronie tylko 25 sesji snu.
  • Ograniczenia zakresu dat w przypadku scalania danych: w przypadku punktów końcowych scalania i agregacji danych (np. rollUp i dailyRollUp) zakresy dat zapytań są ograniczone w zależności od typu danych:
    • Maksymalny zakres 14 dni w przypadku usług calories-in-heart-rate-zone, heart-rate, active-minutes i total-calories.
    • Maksymalny zakres 90 dni w przypadku wszystkich innych typów danych zbiorczych.

W zależności od ilości danych historycznych, których potrzebuje Twoja aplikacja, pobranie całego zbioru danych będzie wymagało sekwencyjnego przechodzenia między stronami. Pamiętaj o tym podczas projektowania procesu synchronizacji danych aplikacji.

Aby zapewnić optymalną wydajność i uniknąć błędów interfejsu API, podczas wysyłania zapytań o dane historyczne postępuj zgodnie z tymi wskazówkami:

Stopniowa synchronizacja danych (wczytywanie gorące i zimne)

  • Początkowe „gorące” wczytywanie: podczas głównej sekwencji wczytywania pobieraj i renderuj tylko dane z ostatnich 7–14 dni. Dzięki temu użytkownicy od razu widzą dane bez czekania na długotrwałe zapytania.
  • Wczytywanie „na zimno” w tle: po wyrenderowaniu głównego interfejsu użytkownika przekieruj pobieranie starszych danych historycznych do asynchronicznej kolejki o niższym priorytecie lub procesu w tle.

Dzielenie zapytań na potrzeby agregacji

  • Ponieważ punkty końcowe zbiorczych i dziennych danych zbiorczych wymuszają limit maksymalnego zakresu dat (14 lub 90 dni w zależności od typu danych), musisz podzielić duże zapytania o historyczne dane zbiorcze na mniejsze, kolejne przedziały czasu mieszczące się w tych limitach.
  • Bezpiecznie grupuj lub sekwencjonuj te zapytania, aby zachować limity współbieżności i utrzymywać stałe wskaźniki postępu interfejsu.

Wykorzystywanie wstępnie zagregowanych podsumowań

Zmień strukturę paneli Ogółem i wykresów trendów, aby korzystać z wstępnie zagregowanych punktów końcowych podsumowania (np. DailyRollUpDataPoints). Znacznie zmniejszy to obciążenie obliczeniowe backendu i czas przesyłania danych do klienta.

Odporna obsługa błędów (inteligentne ponawianie)

  • W przypadku przekroczenia ograniczania liczby żądań (429 Too Many Requests) i limitów czasu bramy serwera (504 Gateway Timeout) stosuj ścisłe wzrastający czas do ponowienia. Nigdy nie ponawiaj od razu prób wysyłania dużych, nieudanych ładunków. Natychmiastowe ponawianie prób zwiększa przeciążenie backendu i pogarsza działanie systemu.