The Sonar API is organized around REST. It accepts JSON-encoded request bodies, returns JSON responses, and uses standard HTTP response codes.

Base URL

All API requests should be made to:

text
https://api.sonarhealth.co/v1

Authentication

Every request must include a Bearer token in the Authorization header:

text
Authorization: Bearer <token>

See Authentication for details on obtaining API keys and JWTs.

Response Format

All successful responses follow a consistent envelope:

json
{
  "data": { ... },
  "meta": {
    "page": 1,
    "per_page": 20,
    "total": 142
  }
}
  • data — the response payload (object or array depending on the endpoint)
  • meta — pagination metadata, included when the response is paginated

Pagination

Offset-Based Pagination

Most endpoints use offset-based pagination with page and per_page query parameters:

Parameter Type Default Description
page int 1 Page number (1-indexed)
per_page int 20 Results per page (max: 100)

The meta object in the response tells you the total count and current position:

json
{
  "meta": {
    "page": 2,
    "per_page": 20,
    "total": 142
  }
}

Cursor-Based Pagination

The Events API uses cursor-based pagination for reliable sequential processing. Pass your last cursor to resume where you left off:

text
GET /v1/events?cursor=evt_01HX3K...&limit=100
Parameter Type Default Description
cursor string Resume from this position (omit to start from the beginning)
limit int 100 Maximum events per page

The response includes the next cursor and a flag indicating whether more events are available:

json
{
  "events": [
    {
      "event_id": "evt_01HX4M9ABC",
      "event_type": "data.updated",
      "metrics": ["resting_heart_rate", "heart_rate_variability", "steps"],
      "user_id": "usr_abc123",
      "device_id": "dev_garmin_456",
      "timestamp": "2026-03-01T14:22:00Z"
    }
  ],
  "cursor": "evt_01HX4M9ABC",
  "has_more": true
}

Store the returned cursor and pass it on the next call. See Data Delivery for event types and delivery patterns.

Common Query Parameters

These parameters work across multiple endpoints:

Parameter Type Description
unit_system string metric (default) or imperial. When set to imperial, distances return in miles, temperatures in Fahrenheit, and weights in pounds.
include_sources boolean When true, each data point includes a source object showing which device contributed the value and its original reading.

Error Handling

Errors return an appropriate HTTP status code and a JSON body:

json
{
  "error": "not_found",
  "message": "The requested resource was not found.",
  "details": {}
}

The details field is always present — it contains additional context when available, or an empty object otherwise.

Status Code Meaning
400 Bad Request — invalid parameters
401 Unauthorized — missing or invalid token
403 Forbidden — insufficient permissions
404 Not Found — resource doesn't exist
429 Too Many Requests — rate limit exceeded
500 Internal Server Error

Content Types

  • Requests — send Content-Type: application/json for all POST/PUT/PATCH bodies
  • Responses — all responses return Content-Type: application/json
  • Dates — all dates use ISO 8601 format (2025-01-15 for dates, 2025-01-15T10:30:00Z for timestamps)

Versioning

The API version is embedded in the URL path (/v1/). Breaking changes will ship under a new version prefix. Non-breaking additions (new fields, new endpoints) are added to the current version without notice.

Common Questions

What are the rate limits?

The API allows 100 requests per second per API key. Bursts up to 200 requests are allowed for short spikes. If you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header. For high-volume use cases, use webhooks to react to changes instead of polling per-user endpoints.

Are requests idempotent?

All GET requests are safe to retry. POST requests to create resources (users, webhooks) are not idempotent — calling them twice creates two resources. Use reference_id on user creation to detect duplicates in your system. Token generation endpoints (/auth/mobile-token, /auth/user-token) are idempotent in the sense that calling them twice issues two valid tokens, which is harmless.

How should I handle retries?

Use exponential backoff with jitter. Start at 1 second, double on each retry, cap at 30 seconds, and add random jitter to avoid thundering herd. Always respect the Retry-After header on 429 responses. For 500 errors, retry up to 3 times — if it persists, the issue is on Sonar's side.

Can I use the API from the browser?

Yes, using a User Token. User Tokens are scoped to a single user and read-only, making them safe for client-side use. Never use your API Key from browser code.