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 (pobieranie zasobu) lub LIST (filtrowanie/stronicowanie), ale jest też używane w przypadku operacji DELETE.

  • Miejsce docelowe: dołączone 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 zmieniają 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, PATCHPUT.

  • Miejsce docelowe: 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 programu AIP Używane do wyszukiwania, filtrowania i operacji odczytu. Używany w operacjach zapisu.
Widoczność Widoczne w historii przeglądarki i dziennikach serwera. Ukryty w adresie URL.
Złożoność Ograniczone do płaskich lub powtarzanych struktur. Obsługuje głęboko zagnieżdżone obiekty JSON.
Kodowanie Musi być zakodowany w formacie adresu URL (np. spacje stają się znakiem %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 dat z uwzględnieniem tych warunków:

  • 4-cyfrowy rok 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 oraz przykłady każdego z nich.

Uzyskiwanie identyfikatora użytkownika Fitbita lub Google

Gdy użytkownik wyrazi zgodę za pomocą Google OAuth 2.0, odpowiedź tokena nie będzie zawierać identyfikatora użytkownika Fitbita ani Google. Aby uzyskać identyfikator użytkownika, wywołaj getIdentitypunkt końcowy. getIdentity zwraca zarówno starszy identyfikator użytkownika Fitbita, jak i identyfikator użytkownika Google.

Zalecamy, aby po wyrażeniu przez nowego użytkownika zgody za pomocą OAuth wywołać punkt końcowy getIdentity i zapisać oba identyfikatory użytkownika. Zapewnia to zgodność wsteczną i przyszłą 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 częściowych lub szczegółowych zebranych w ciągu dnia

Użyj listpunktu 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 wiarygodny, 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 listreconcile 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 wiarygodny rekord) 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 tej 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, rollUpdailyRollUp znajdziesz w sekcji Filtrowanie według rodziny źródeł danych w przewodniku Filtrowanie danych.

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

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 fizycznych urządzeń do noszenia a z wpisów ręcznych).

Punkty końcowe reconcile, rollUpdailyRollUp 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 JSON.
dailyRollUp (POST) Przekaż dataSourceFamily jako pole w treści żądania JSON.

Obsługiwane rodziny źródeł danych

W tabeli poniżej znajdziesz obsługiwane 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 będą 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). Nie obejmuje danych rejestrowanych ręcznie ani danych szacowanych 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 2026-03-03:

Żą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.

To żądanie POST wysyła zapytanie o liczbę kroków z chodzenia w ciągu dnia w odstępach 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 określonej 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 liczby 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"
      }
    }
  ]
}

Zbieranie punktów danych w określonym przedziale czasu

Użyj rollUp punktu 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 błędem 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 bazowej typu danych (np. "60s" w przypadku 1-minutowych przedziałów). Więcej informacji znajdziesz w artykule Rozmiar okna zbiorczego i rozdzielczość pamięci bazowej.

Aby na przykład zsumować liczbę kroków w 1-minutowych odstępach czasu (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"
      }
    },
...
  ]
}

Łączenie danych z jednego lub wielu dni

dailyRollUp Punktu końcowego należy używać, gdy chcesz agregować dane z jednego lub wielu dni, czyli z 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.

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 grupy, 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 Twoją prośbę bez modyfikacji i nie wykonuje zaokrąglania, przesunięć czasowych 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 przycięte (ograniczone) do czasu zakończenia zakresu.

Jak działa podział na grupy

Gdy wysyłasz żądanie dotyczące zbiorczych danych z zakresów, które nie są podzielne, interfejs API stosuje te reguły:

  • Podział na przedziały rozpoczyna się na początku żądanego zakresu (range.startTime lub range.start) i przebiega dalej o rozmiar okna (windowSize lub windowSizeDays).
  • Ostatni przedział czasowy 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ą wyraźnie określone własne sygnatury czasowe rozpoczęcia i zakończenia, których możesz użyć do sprawdzenia rzeczywistego czasu trwania obciętego zasobu.
  • Interfejs API zwraca dane zbiorcze w kolejności odwrotnej do 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: 12-minutowy zakres z 5-minutowym oknem

Załóżmy, że klient prosi o zestawienie danych 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 powstają 3 przedziały czasowe:

  1. Koszyk 1: [10:00:00, 10:05:00) – czas trwania: 5 minut (pełne okno)
  2. Kosz 2: [10:05:00, 10:10:00) – czas trwania: 5 minut (pełny okres)
  3. Kosz 3 (skrócony): [10:10:00, 10:12:00) – czas trwania: 2 minuty (skrócony do range.endTime)

Wpływ na wartości zagregowane

Ponieważ ostatni przedział 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 z powodu krótszego czasu trwania.

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

  • Kosz 1 (10:00–10:05): 500 kroków (5 minut × 100 kroków/minutę)
  • Kosz 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 dowolną wartość 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 czasowych w pamięci bazowej. Na przykład dane dotyczące aktywności fizycznej rejestrowane przez urządzenia do noszenia, takie jak steps, distance, active-minutesactive-energy-burned, są zwykle rejestrowane 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 rozprowadza danych interwałowych między zasobniki podinterwałów.

Jeśli określisz windowSize mniejszy niż interwał przechowywania danych bazowych (np. poprosisz o 10-sekundowe okno dla steps przechowywanych w interwałach 1-minutowych):

  1. Pierwszy podzakres pasujący do przedziału startTime (np. 10:00:0010: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:1010:00:20, 10:00:2010:00:30 itd.) nie otrzymują żadnych punktów danych, ponieważ w tych przedziałach nie rozpoczyna się żaden interwał.

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

Aby uzyskać równomiernie rozłożone i znaczące wartości zagregowane, zawsze ustawiaj parametr windowSize na czas trwania równy lub większy niż rozdzielczość pamięci masowej bazowego typu danych docelowych (np. 60s lub większy w przypadku steps). Rozdzielczość pamięci masowej i minimalne zalecane okno podsumowania dla każdego typu danych znajdziesz w dokumentacji typów 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 (startTimeendTime) mogą być też aktualizowane przez właściciela rekordu lub propagowane z platform nadrzędnych, takich jak Health Connect. Szczegółowe informacje 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 dla istniejących danych.

Kiedy używać identyfikatora punktu danych

Identyfikator punktu danych jest niezbędny w tych sytuacjach:

  • Aktualizacje kierowane: 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 „HumanScale” firmy „Scales R Us”. Nowy odczyt tkanki tłuszczowej użytkownika to 20% – data: 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ć wartość startTime lub endTime istniejącego punktu danych interwału, 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.

Ten przykład pokazuje, jak aplikacja właściciela aktualizuje sygnatury czasowe interwałów 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 element 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żywanie user-iddata-point-id z pierwotnej funkcji przyczepów:

Żą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 punktu końcowego list, 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ń o dane historyczne

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 częstotliwości. Aby zarządzać stabilnością systemu i zapobiegać nadmiernym rozmiarom pakietów danych, interfejs Google Health API używa automatycznego stronicowania z rozmiarami stron dostosowanymi do konkretnych punktów końcowych. Pamiętaj o tych granicach i sposobie 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 exercisesleep, 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. rollUpdailyRollUp) 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-minutestotal-calories.
    • Maksymalny zakres 90 dni w przypadku wszystkich innych rodzajó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ć sekwencyjnego przechodzenia przez strony. Pamiętaj o tym podczas projektowania procesu synchronizacji danych w aplikacji.

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

Synchronizacja danych etapami (gorące ładowanie i wczytywanie „na zimno”)

  • Początkowe wczytywanie „na gorąco”: 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: przekazywanie starszych danych historycznych do asynchronicznej kolejki o niższym priorytecie lub procesu w tle po wyrenderowaniu głównego interfejsu.

Dzielenie zapytań na potrzeby agregacji

  • Ponieważ punkty końcowe zbiorczych i dziennych danych zbiorczych wymuszają maksymalny zakres 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 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ólnych 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.