نقطه‌های پایانی

این صفحه مروری بر قراردادهای REST API، به همراه فهرستی از وظایف رایج Google Health API و نمونه‌هایی از هر کدام ارائه می‌دهد.

قراردادهای REST API

رابط برنامه‌نویسی کاربردی گوگل هلث (Google Health API) از استانداردهای پیشنهادهای بهبود رابط کاربری گوگل (AIP) ، به‌ویژه AIP-127 (ترانس‌کدینگ HTTP و gRPC) و AIP-131 تا AIP-135 (روش‌های استاندارد) پیروی می‌کند. این استانداردها نحوه نگاشت داده‌ها از یک پیام اولیه به یک درخواست HTTP را تعریف می‌کنند.

پارامترهای پرس و جو

پارامترهای پرس‌وجو زمانی استفاده می‌شوند که داده‌ها بخشی از URL باشند. این در درجه اول برای درخواست‌های GET (دریافت یک منبع) یا درخواست‌های LIST (فیلتر کردن/صفحه‌بندی) است، اما برای عملیات DELETE نیز استفاده می‌شود.

  • محل قرارگیری : بعد از علامت ? به آدرس اینترنتی (URL) اضافه می‌شود.
  • نحو : جفت‌های کلید-مقدار با & از هم جدا می‌شوند.
  • نگاشت : هر فیلدی در پیام درخواست که بخشی از الگوی مسیر URL نیست، به یک پارامتر پرس‌وجو نگاشت می‌شود.
  • مناسب برای : انواع داده ساده (رشته‌ها، اعداد صحیح، اعداد شمارشی) و فیلدهای تکراری.

مثال نحوی:

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"

درخواست بدنه

بدنه درخواست زمانی استفاده می‌شود که داده‌ها وضعیت یک منبع را تغییر می‌دهند یا برای یک URL خیلی بزرگ هستند. بدنه معمولاً یک نمایش JSON از خود منبع است. معمولاً برای عملیات POST ، PATCH و PUT استفاده می‌شود.

  • محل قرارگیری : درون بار داده HTTP (در URL قابل مشاهده نیست).
  • سینتکس : به صورت یک شیء JSON قالب‌بندی شده است.
  • نگاشت : در حاشیه‌نویسی google.api.http تعریف شده است.
    • body: "*" به این معنی است که کل پیام بدنه است.
    • body: "resource_name" یعنی فقط یک فیلد خاص در پروتو، بدنه است.
  • بهترین برای : اشیاء پیچیده، پیام‌های تو در تو و داده‌های حساس.

مثال نحوی:

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

مورد ترکیبی

در یک متد Update منطبق با AIP-134 یا یک عملیات PATCH ، هر دو استفاده می‌شوند. URL شامل نام منبع، بدنه شامل داده‌های منبع به‌روزرسانی‌شده و یک پارامتر پرس‌وجو (معمولاً update_mask ) مشخص می‌کند که کدام فیلدها باید تغییر کنند.

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

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

تفاوت‌های کلیدی در یک نگاه

ویژگی پارامترهای پرس و جو درخواست بدنه
راهنمایی AIP برای عملیات جستجو، فیلتر کردن و خواندن استفاده می‌شود. برای عملیات نوشتن استفاده می‌شود.
قابلیت مشاهده در تاریخچه مرورگر و لاگ‌های سرور قابل مشاهده است. از آدرس اینترنتی (URL) پنهان شده است.
پیچیدگی محدود به ساختارهای مسطح یا تکراری. از اشیاء JSON تو در تو و عمیق پشتیبانی می‌کند.
رمزگذاری باید با URL کدگذاری شود (برای مثال، فاصله‌ها به %20 تبدیل می‌شوند). کدگذاری استاندارد JSON

خرما

تمام تاریخ‌ها در API گوگل هلث با فرمت YYYY-MM-DD نمایش داده می‌شوند. API تغذیه از استاندارد ISO-8601 برای مقادیر تاریخ با شرایط زیر پشتیبانی می‌کند:

  • یک سال چهار رقمی YYYY
  • مقادیر سال در محدوده ۰۰۰۰-۹۹۹۹
  • عدم اعمال محدودیت‌های تاریخ شروع مندرج در استاندارد ISO-8601 یا سایر استانداردهای دوره‌ای

سربرگ‌ها

اجرای نقاط پایانی API گوگل هلث نیاز به استفاده از هدرها و توکن دسترسی مناسب دارد. هدر زیر برای هر دو درخواست GET و POST توصیه می‌شود:

Authorization: Bearer access-token
Accept: application/json

فهرست وظایف API

این بخش فهرستی از وظایف رایج API سلامت گوگل و نمونه‌هایی از هر کدام را ارائه می‌دهد.

شناسه کاربری Fitbit یا Google را دریافت کنید

پس از اینکه کاربر از طریق Google OAuth 2.0 رضایت خود را اعلام کرد، پاسخ توکن حاوی شناسه کاربری Fitbit یا Google نیست. برای دریافت شناسه کاربری، نقطه پایانی getIdentity را فراخوانی کنید. getIdentity هم شناسه کاربری قدیمی Fitbit و هم شناسه کاربری Google را برمی‌گرداند.

توصیه می‌کنیم به محض اینکه کاربر جدید از طریق OAuth موافقت خود را اعلام کرد، نقطه پایانی getIdentity را فراخوانی کرده و هر دو شناسه کاربر را ذخیره کنید. این کار سازگاری رو به عقب و رو به جلو را در ادغام شما فراهم می‌کند.

برای مثال:

درخواست

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

پاسخ

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

داده‌های روزانه یا داده‌های دقیقی را که در طول روز جمع‌آوری شده‌اند، دریافت کنید

از نقطه پایانی list برای یک نوع داده خاص استفاده کنید تا داده‌های درون‌روزی یا جزئی جمع‌آوری‌شده در طول روز را در فواصل پشتیبانی‌شده برای آن نوع داده دریافت کنید.

برای مثال:

درخواست

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

پاسخ

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

یک نمای تطبیقی ​​از داده‌های بازه دریافت کنید

برای بازیابی داده‌های بازه زمانی بدون همپوشانی رکوردها یا تداخل چند دستگاهی، نقطه پایانی reconcile فراخوانی کنید. نقطه پایانی تطبیق به طور خودکار فواصل زمانی همپوشانی را در دسته‌های همگام‌سازی و چندین دستگاه ضبط حذف می‌کند و یک جریان معتبر و پیوسته مناسب برای رندر کردن جدول زمانی فعالیت‌ها و محاسبه مدت زمان‌ها را برمی‌گرداند.

برای پیش‌زمینه در مورد اینکه چرا دستگاه‌های متصل فواصل همپوشانی ایجاد می‌کنند و مقایسه عملیاتی بین list و reconcile ، به راهنمای مدیریت داده‌ها مراجعه کنید.

مثال زیر پاسخ list (که هر دو رکورد همپوشانی را برمی‌گرداند) را در مقابل reconcile (که با بازگرداندن رکورد معتبر، تداخل را حل می‌کند) برای کاربری با دو جلسه تمرین همپوشانی مقایسه می‌کند:

فهرست خام

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

آشتی داده شده

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

تطبیق، جلسات متناقض را با حذف داده‌های تکراری و انتخاب رکورد معتبر به جای ترکیب یک اتحاد زمانی مصنوعی (مانند 11:00:00Z تا 11:50:00Z ) حل می‌کند. پاسخ تطبیق داده شده، نقطه داده برنده ( 7797422996486764704 ) را به همراه بازه ثبت شده اصلی آن ( 11:20:00Z تا 11:50:00Z ) برمی‌گرداند و یکپارچگی تله‌متری اندازه‌گیری شده و معیارهای آن جلسه را حفظ می‌کند.

فیلتر کردن داده‌ها

برای بازیابی زیرمجموعه‌های خاصی از رکوردهای نقطه داده که با معیارهایی مانند فاصله زمانی، تاریخ یا زمان مشاهده مطابقت دارند، از list استفاده کنید یا نقطه پایانی را با یک پارامتر filter reconcile .

برای دستورالعمل‌های دقیق، قوانین قالب‌بندی، خطاهای اعتبارسنجی و نمونه‌های پرس‌وجو، به راهنمای فیلتر کردن داده‌ها مراجعه کنید.

فیلتر بر اساس خانواده منبع داده

برای جداسازی یا تجمیع داده‌ها از انواع خاص منابع (برای مثال، دستگاه‌های پوشیدنی فیزیکی در مقابل ورودی‌های دستی)، از پارامتر dataSourceFamily استفاده کنید.

برای دستورالعمل‌های دقیق، فمیلی‌های پشتیبانی‌شده و مثال‌های درخواست و پاسخ برای reconcile ، rollUp و dailyRollUp ، به بخش فیلتر بر اساس فمیلی منبع داده در راهنمای فیلتر کردن داده‌ها مراجعه کنید.

فیلتر کردن داده‌ها بر اساس زمان شروع مدنی با فاصله زمانی

از نقطه پایانی list به همراه یک پارامتر filter برای فیلتر کردن داده‌ها بر اساس زمان مدنی یا یک بازه زمانی استفاده کنید.

برای مثال:

درخواست

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

پاسخ

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

فیلتر کردن داده‌ها بر اساس زمان فیزیکی مشاهده نمونه

از نقطه پایانی list به همراه یک پارامتر filter برای فیلتر کردن داده‌ها بر اساس زمان فیزیکی مشاهده نمونه استفاده کنید.

برای مثال:

درخواست

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

پاسخ

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

فیلتر کردن و تجمیع بر اساس خانواده منبع داده

یک خانواده منبع داده، یک گروه‌بندی منطقی از منابع داده (مانند ساعت‌های هوشمند، برنامه‌های تلفن همراه یا ورودی‌های دستی) است. این خانواده به شما امکان می‌دهد داده‌ها را از انواع خاصی از منابع (به عنوان مثال، دستگاه‌های پوشیدنی فیزیکی در مقابل ورودی‌های دستی) جدا یا جمع‌آوری کنید.

نقاط پایانی reconcile ، rollUp و dailyRollUp همگی از پارامتر dataSourceFamily پشتیبانی می‌کنند. مکانیسم ارسال به نقطه پایانی بستگی دارد:

نقطه پایانی (روش HTTP) مکانیسم
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 شامل داده‌های ثبت‌شده توسط دستگاه‌های ردیاب گوگل و فیت‌بیت (مانند ردیاب‌های پوشیدنی فیت‌بیت و پیکسل واچ) می‌شود. داده‌های ثبت‌شده دستی و داده‌های تخمینی تلفن را شامل نمی‌شود. از این گزینه زمانی استفاده کنید که یکپارچه‌سازی شما نیاز به تله‌متری خام حسگر دارد که مستقیماً توسط سخت‌افزار پوشیدنی ثبت شده باشد.
users/me/dataSourceFamilies/google-sources شامل منابع شخص ثالث گوگل و فیت‌بیت می‌شود. این شامل سوابق دستگاه‌های ردیاب فیزیکی، داده‌های Health Connect و هرگونه ورودی دستی ثبت‌شده از طریق برنامه‌های شخص ثالث (مانند برنامه Fitbit یا Google Fit) می‌شود.

برای دریافت یک جریان داده تطبیقی ​​از یک خانواده منبع داده خاص، نقطه پایانی reconcile را با پارامتر پرس و جوی dataSourceFamily فراخوانی کنید.

برای مثال، درخواست GET زیر، خواب ثبت‌شده توسط ردیاب را برای روز بعد از تاریخ 2026-03-03 واکشی می‌کند:

درخواست

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 را فراخوانی کنید و فیلد dataSourceFamily را در بدنه درخواست JSON ارسال کنید.

درخواست 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 را در بدنه درخواست ارسال کنید.

برای مثال، درخواست زیر، جمع‌بندی روزانه‌ی تعداد قدم‌های کاربر، شامل تمام منابع شخص ثالث گوگل و فیت‌بیت (پوشیدنی‌ها + ورودی‌های دستی) را محاسبه می‌کند:

درخواست

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

جمع‌آوری نقاط داده در یک بازه زمانی مشخص

از نقطه پایانی rollUp برای بازگرداندن مجموع نقاط داده بر اساس یک پنجره در واحد ثانیه، در محدوده datetime بر اساس زمان فیزیکی کاربر (به UTC) استفاده کنید.

هنگام فراخوانی نقطه پایانی rollUp ، بدنه درخواست را که نشان‌دهنده محدوده زمانی مورد نیاز و windowSize است، ارائه دهید. به الزامات زیر برای windowSize توجه کنید:

  • حداقل اندازه پنجره: مدت زمان windowSize باید حداقل ۱ ثانیه ( "1s" ) باشد. مدت زمان‌های زیر یک ثانیه، صفر یا منفی با 400 Bad Request ( INVALID_ROLLUP_WINDOW ) رد می‌شوند.
  • ترازبندی وضوح ذخیره‌سازی: برای جلوگیری از توزیع ناهموار داده‌های تجمیع‌شده در زیرباکت‌ها، windowSize انتخاب کنید که برابر یا بزرگتر از وضوح ذخیره‌سازی زیربنایی نوع داده باشد (مانند "60s" برای فواصل گام 1 دقیقه‌ای). برای جزئیات بیشتر، به اندازه پنجره Rollup و وضوح ذخیره‌سازی زیربنایی مراجعه کنید.

برای مثال، برای جمع کردن تعداد گام‌ها در فواصل ۱ دقیقه‌ای ( 60s ):

درخواست

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

پاسخ

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

جمع‌آوری داده‌ها در طول یک روز یا چند روز

نقطه پایانی dailyRollUp باید زمانی استفاده شود که می‌خواهید داده‌ها را در طول یک روز یا چند روز، که به عنوان windowSize شناخته می‌شود، جمع‌آوری کنید. محدوده زمانی مدنی بسته-باز را برای بازه مورد نیاز در بدنه درخواست ارائه دهید. بسته به نوع داده، یا مجموع یا میانگین را در بازه دریافت خواهید کرد.

برای مثال:

درخواست

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
}

پاسخ

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

سطل بندی زمانی که محدوده مضربی از اندازه پنجره نیست

اگر محدوده درخواستی مضرب دقیقی از windowSize (یا windowSizeDays ) نباشد، آخرین سطل به ترتیب زمانی در نقطه انتهایی بالایی محدوده کوتاه می‌شود و مدت زمانی کوتاه‌تر از اندازه پنجره را پوشش می‌دهد. API درخواست شما را بدون تغییر می‌پذیرد و هیچ گونه گرد کردن، تغییر زمان یا درون‌یابی داده‌ای انجام نمی‌دهد.

برای پوشش کل محدوده درخواستی، API از تقسیم سقف برای محاسبه تعداد کل پنجره‌های تجمیع استفاده می‌کند:

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

هر سطل به ترتیب از ابتدای محدوده شما شروع می‌شود. اگر اضافه کردن یک پنجره کامل دیگر، از زمان پایان درخواستی شما فراتر رود، پنجره نهایی در زمان پایان محدوده کوتاه (clamped) می‌شود.

نحوه کار سطل زنی

هنگام درخواست جمع‌بندی با محدوده‌های غیرقابل تقسیم، API قوانین زیر را اعمال می‌کند:

  • Bucketing از ابتدای محدوده درخواستی شما ( range.startTime یا range.start ) شروع می‌شود و به اندازه اندازه پنجره ( windowSize یا windowSizeDays ) به جلو می‌رود.
  • آخرین سطل زمانی در انتهای بازه درخواستی شما ( range.endTime یا range.end ) قرار می‌گیرد، به این معنی که مدت زمان کوتاه‌تری نسبت به اندازه پنجره درخواستی را پوشش می‌دهد.
  • اشیاء RollupDataPoint یا DailyRollupDataPoint برگردانده شده، به صراحت مهرهای زمانی شروع و پایان خود را مشخص می‌کنند که می‌توانید از آنها برای بررسی مدت زمان واقعی سطل کوتاه شده استفاده کنید.
  • از آنجا که API داده‌های rollup را به ترتیب زمانی معکوس (جدیدترین‌ها اول) برمی‌گرداند، آخرین سطل زمانی (که سطل کوتاه‌شده است) به عنوان اولین عنصر ( index 0 ) در لیست برگردانده شده ظاهر می‌شود.

سناریو: برد ۱۲ دقیقه‌ای با بازه زمانی ۵ دقیقه‌ای

فرض کنید یک کلاینت درخواست جمع‌بندی در یک بازه ۱۲ دقیقه‌ای با windowSize ۵ دقیقه‌ای را دارد:

  • range.startTime : 10:00:00
  • range.endTime : 10:12:00 (مدت زمان کل: 12 دقیقه)
  • windowSize : 5 minutes

از آنجایی که ۱۲ دقیقه مضربی از ۵ دقیقه نیست (۱۲ = ۵ * ۲ + ۲)، API درخواست را می‌پذیرد و تعداد پنجره‌ها را به صورت ceiling(12 / 5) = 3 محاسبه می‌کند.

این سه دسته زمانی زیر را تولید می‌کند:

  1. دسته ۱: [10:00:00, 10:05:00) — مدت زمان: ۵ دقیقه (تمام پنجره)
  2. دسته دوم: [10:05:00, 10:10:00) — مدت زمان: ۵ دقیقه (تمام پنجره)
  3. سطل ۳ (کوتاه شده): [10:10:00, 10:12:00) — مدت زمان: ۲ دقیقه (کوتاه شده در range.endTime )

تأثیر بر مقادیر تجمیع‌شده

از آنجا که پنجره نهایی مدت زمان کوتاه‌تری دارد، معیارهای افزایشی (مانند مجموع یا تعداد مراحل) صرفاً به دلیل مسیر زمانی کوتاه‌تر، در سطل کوتاه‌شده پایین‌تر خواهند بود.

اگر کاربری در کل این بازه ۱۲ دقیقه‌ای با سرعت ثابت ۱۰۰ قدم در دقیقه راه برود:

  • دسته ۱ (۱۰:۰۰–۱۰:۰۵): ۵۰۰ قدم (۵ دقیقه × ۱۰۰ قدم در دقیقه)
  • دسته دوم (۱۰:۰۵–۱۰:۱۰): ۵۰۰ قدم (۵ دقیقه × ۱۰۰ قدم در دقیقه)
  • دسته ۳ (۱۰:۱۰–۱۰:۱۲): ۲۰۰ قدم (۲ دقیقه × ۱۰۰ قدم در دقیقه)

نمونه پاسخ API که ترتیب را نشان می‌دهد

از آنجا که API نتایج را به ترتیب زمانی معکوس برمی‌گرداند، سطل کوتاه شده به عنوان اولین عنصر در لیست برگردانده شده ظاهر می‌شود:

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

اندازه پنجره Rollup و وضوح ذخیره‌سازی زیربنایی

در حالی که نقطه پایانی rollUp هر windowSize با ۱ ثانیه یا بیشتر را می‌پذیرد، انواع مختلف داده، اندازه‌گیری‌ها را با نرخ‌های نمونه‌برداری یا مدت زمان‌های فاصله‌ای مختلف در حافظه اصلی ثبت و ذخیره می‌کنند. به عنوان مثال، معیارهای فعالیت بدنی پوشیدنی مانند steps ، distance ، active-minutes و active-energy-burned معمولاً در فواصل ۱ دقیقه‌ای ( 60s ) ثبت می‌شوند.

هنگام تجمیع انواع داده‌های بازه، نقطه پایانی rollUp هر نقطه داده ثبت شده را در سطلی که حاوی startTime آن نقطه داده است قرار می‌دهد. این API داده‌های بازه را در سطل‌های زیربازه برش، درون‌یابی یا توزیع نمی‌کند.

اگر windowSize مشخص کنید که کوچکتر از فاصله ذخیره‌سازی داده‌های اصلی باشد (برای مثال، درخواست یک پنجره ۱۰ ثانیه‌ای برای steps ذخیره شده در فواصل ۱ دقیقه‌ای):

  1. اولین زیر-باکتی که با startTime بازه (برای مثال، 10:00:00 تا 10:00:10 ) مطابقت دارد، کل شمارش انباشته‌شده دقیقه (برای مثال، تمام ۱۰۰ گام ثبت‌شده برای آن دقیقه) را دریافت می‌کند.
  2. زیر-باکت‌های باقی‌مانده در همان دقیقه ( 10:00:10 تا 10:00:20 ، 10:00:20 تا 10:00:30 و غیره) هیچ نقطه داده‌ای دریافت نمی‌کنند، زیرا هیچ بازه زمانی در آن پنجره‌ها شروع نمی‌شود.

این منجر به داده‌های «ناهموار» می‌شود که در آن مقدار کل بازه در زیرپنجره اول متمرکز شده است.

برای به دست آوردن داده‌های تجمعی با توزیع یکنواخت و معنادار، همیشه مدت زمان windowSize را برابر یا بزرگتر از وضوح ذخیره‌سازی زیربنایی نوع داده هدف تنظیم کنید (برای مثال، 60s یا بیشتر برای steps ). برای وضوح ذخیره‌سازی و حداقل پنجره جمع‌بندی توصیه‌شده برای هر نوع داده، به مرجع انواع داده Google Health API مراجعه کنید.

به‌روزرسانی داده‌های سلامت کاربر

از نقطه پایانی patch برای به‌روزرسانی داده‌های سلامت کاربر استفاده کنید.

نقطه پایانی patch یک رکورد موجود را بر اساس شناسه مشخص شده در URL درخواست، به‌روزرسانی می‌کند. شناسه یک نقطه داده قبلاً درج شده را ارائه دهید. API رکورد موجود را بازنویسی می‌کند.

مهرهای زمانی بازه‌های یک نقطه داده ( startTime و endTime ) می‌توانند توسط صاحب رکورد به‌روزرسانی شوند یا از پلتفرم‌های بالادستی مانند Health Connect منتشر شوند. برای جزئیات بیشتر در مورد تغییرپذیری مهرهای زمانی، به راهنمای مدیریت داده‌ها مراجعه کنید. برای مثالی از به‌روزرسانی مهرهای زمانی بازه، به Update interval timestamps for existing data مراجعه کنید.

چه زمانی از شناسه نقطه داده استفاده کنیم

شناسه نقطه داده در سناریوهای زیر ضروری است:

  • به‌روزرسانی‌های هدفمند: برای به‌روزرسانی یک معیار خاص، شناسه آن را در درخواست patch ارائه دهید.
  • حذف‌ها: حفظ شناسه به برنامه شما اجازه می‌دهد تا بعداً با استفاده از نقطه پایانی batchDelete رکورد را حذف کند.

در اینجا مثالی آورده شده است که در آن کاربر میزان چربی بدن خود را در ترازویی به نام "HumanScale" از شرکت "Scales R Us" به‌روزرسانی می‌کند. میزان چربی بدن جدید کاربر برای تاریخ 2026-03-10، 20٪ است:

درخواست

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

پاسخ

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

به‌روزرسانی مهرهای زمانی فاصله‌ای برای داده‌های موجود

برای به‌روزرسانی startTime یا endTime یک نقطه داده بازه موجود، یک درخواست PATCH به URI منبع نقطه داده ارسال کنید. فقط سازنده یا مالک اصلی یک رکورد می‌تواند فیلدهای آن را تغییر دهد. برنامه‌ها نمی‌توانند نقاط داده‌ای را که ایجاد نکرده‌اند ویرایش کنند.

برای پیش‌زمینه در مورد تغییرپذیری مهر زمانی، به‌روزرسانی‌های بالادستی از Health Connect و پیامدهای ذخیره‌سازی، به راهنمای مدیریت داده‌ها مراجعه کنید.

مثال زیر یک برنامه مالک را نشان می‌دهد که با استفاده از نقطه پایانی patch مهرهای زمانی فاصله‌ای یک گزارش هیدراتاسیون موجود را به‌روزرسانی می‌کند:

درخواست

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

پاسخ

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

یک ماده غذایی را ثبت کنید

برای ثبت یک ماده غذایی، یک درخواست POST به نقطه پایانی nutrition-log dataPoints ارسال کنید. بدنه درخواست شامل یک DataPoint با یک شیء nutritionLog است. برای اطلاعات بیشتر، به راهنمای Nutrition مراجعه کنید.

برای مثال:

درخواست

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

پاسخ

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

حذف داده‌های سلامت کاربر

از متد batchDelete برای حذف آرایه‌ای از داده‌های برنامه Fitbit کاربر استفاده کنید.

در اینجا مثالی آورده شده است که در آن کاربر قبلاً چربی بدن خود را روی ترازو ثبت کرده است، اما می‌خواهد این رکورد را حذف کند. با استفاده از user-id و data-point-id از عمل درج اصلی:

درخواست

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

پاسخ

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

اطلاعات دستگاه را پیدا کنید

از نقطه پایانی list برای بازیابی لیست دستگاه‌های جفت‌شده با حساب کاربری استفاده کنید. این شامل اطلاعات مدل دستگاه ( deviceVersion ) و آخرین باری که با برنامه موبایل Google Health ( lastSyncTime ) همگام‌سازی شده است، می‌شود.

پیکربندی لیست و اطلاعات همگام‌سازی برای عیب‌یابی مشکلات همگام‌سازی یا بازیابی داده‌های تاریخی از آخرین زمان همگام‌سازی مفید است.

برای مثال:

درخواست

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

پاسخ

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

پرس و جو از داده‌های تاریخی

یکی از مزایای اصلی API گوگل هلث، قابلیت ردیابی عملکرد کاربر و نظارت بر علائم حیاتی سلامت او در بازه‌های زمانی طولانی است. می‌توانید داده‌های کاربر را از زمانی که ثبت شده است، جستجو کنید؛ این API هیچ محدودیت یا قیدی بر میزان داده‌های تاریخی که برنامه شما می‌تواند مصرف کند، اعمال نمی‌کند.

با این حال، جستجوی داده‌های تاریخی هنوز هم توسط محدودیت‌های نرخ استاندارد اداره می‌شود. برای مدیریت پایداری سیستم و جلوگیری از بارگذاری بیش از حد، API سلامت گوگل از صفحه‌بندی خودکار با اندازه‌های صفحه خاص برای هر نقطه پایانی استفاده می‌کند. به مرزها و رفتار زیر توجه کنید:

  • صفحه‌بندی خودکار: اگر شما یک بازه طولانی از داده‌ها را جستجو کنید، API فقط صفحه اول نتایج را تا حداکثر اندازه صفحه برای آن نقطه پایانی، همراه با nextPageToken برمی‌گرداند. برای درخواست صفحات بعدی باید از nextPageToken استفاده کنید.
  • اندازه‌های متغیر صفحه: محدودیت‌های محدودیت به نقطه پایانی و نوع داده بستگی دارد. برای اکثر انواع داده، اندازه صفحه حداکثر 10000 است. با این حال، برای انواع داده خاصی مانند exercise و sleep ، اندازه پیش‌فرض و حداکثر صفحه 25 است. به عنوان مثال، اگر یک کلاینت تمام داده‌های خواب را برای 10 سال گذشته درخواست کند، API همچنان فقط 25 جلسه خواب را در صفحه اول برمی‌گرداند.
  • محدودیت‌های محدوده تاریخ جمع‌بندی: برای نقاط پایانی جمع‌بندی و تجمیع داده‌ها (مانند rollUp و dailyRollUp )، محدوده‌های تاریخ پرس‌وجو بر اساس نوع داده محدود می‌شوند:
    • حداکثر بازه زمانی ۱۴ روز برای calories-in-heart-rate-zone ، heart-rate ، active-minutes و total-calories .
    • حداکثر بازه زمانی ۹۰ روز برای سایر انواع داده‌های rollup.

بسته به حجم داده‌های تاریخی مورد نیاز برنامه شما، بازیابی کل مجموعه داده‌ها نیاز به صفحه‌بندی متوالی صفحات دارد. این نکته را هنگام طراحی فرآیند همگام‌سازی داده‌های برنامه خود در نظر داشته باشید.

برای اطمینان از عملکرد بهینه و جلوگیری از خطاهای API، هنگام جستجوی داده‌های تاریخی، این دستورالعمل‌ها را دنبال کنید:

همگام‌سازی مرحله‌ای داده‌ها (بار گرم در مقابل بار سرد)

  • بارگذاری "داغ" اولیه: در طول توالی بارگذاری اولیه، فقط داده‌های ۷ تا ۱۴ روز اخیر را دریافت و رندر کنید. این تضمین می‌کند که کاربران داده‌ها را فوراً و بدون انتظار برای کوئری‌های طولانی مدت مشاهده می‌کنند.
  • بارگذاری «سرد» پس‌زمینه: بازیابی داده‌های قدیمی‌تر را پس از رندر شدن رابط کاربری اصلی، به یک صف ناهمزمان و با اولویت پایین‌تر یا فرآیند پس‌زمینه واگذار کنید.

قطعه‌بندی پرس‌وجو برای تجمیع

  • از آنجا که نقاط پایانی rollup و daily rollup حداکثر محدوده تاریخ (۱۴ یا ۹۰ روز بسته به نوع داده) را اعمال می‌کنند، شما باید پرس‌وجوهای تجمیع تاریخی بزرگ را به فواصل زمانی کوچک‌تر و متوالی در این محدوده‌ها تقسیم کنید.
  • این زیرپرس‌وجوها را با خیال راحت دسته‌بندی یا مرتب کنید تا محدودیت‌های همزمانی رعایت شود و شاخص‌های پیشرفت رابط کاربری ثابتی حفظ شوند.

از رول‌آپ‌های از پیش تجمیع‌شده استفاده کنید

داشبوردهای نمای کلی و نمودارهای روند را برای استفاده از نقاط پایانی از پیش تجمیع‌شده و خلاصه (مانند DailyRollUpDataPoints ) بازسازی کنید. این کار به طور چشمگیری سربار محاسباتی در backend و زمان انتقال شبکه به کلاینت را کاهش می‌دهد.

مدیریت خطای انعطاف‌پذیر (تلاش‌های مجدد هوشمند)

  • هنگام مواجهه با محدودیت‌های نرخ ( 429 Too Many Requests ) و زمان‌های وقفه دروازه سرور ( 504 Gateway Timeout ) ، مدیریت دقیق بازگشت نمایی را پیاده‌سازی کنید. هرگز بلافاصله بارهای بزرگ و ناموفق را دوباره امتحان نکنید. تلاش‌های مجدد فوری، ازدحام backend را چند برابر کرده و باعث تخریب سیستم می‌شوند.