```
## OpenAI
```bash [Curl] theme={null}
curl -X POST "https://api.openai.com/v1/responses" \
-H "Authorization: Bearer OPENAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "o4-mini",
"instructions": "you are a health and wellness analyst.",
"input": "Analyze my health data for 2025-08-17 and provide a summary of my health data.",
"tools": [
{
"type": "mcp",
"server_label": "spike-health-data",
"server_url": "https://app-api.spikeapi.com/v3/mcp",
"headers": {
"Authorization": "Bearer SPIKE_ACCESS_TOKEN"
},
"server_description": "Health and fitness data analysis server providing daily and hourly statistics from connected wearables and health devices.",
"require_approval": "never"
}
],
"tool_choice": "auto",
"max_tool_calls": 1,
"max_output_tokens": 5000,
"parallel_tool_calls": true,
"metadata": {
"analysis_type": "daily_health_review",
"date": "2025-08-17",
"tools_used": "mcp_health_data",
"version": "1.0.0"
}
}'
```
```go [Go] theme={null}
package main
import (
"context"
"fmt"
"log"
"os"
openai "github.com/openai/openai-go/v2"
"github.com/openai/openai-go/v2/option"
"github.com/openai/openai-go/v2/responses"
)
func main() {
// Required environment variables
openaiKey := os.Getenv("OPENAI_API_KEY")
spikeToken := os.Getenv("SPIKE_ACCESS_TOKEN")
if openaiKey == "" || spikeToken == "" {
log.Fatal("OPENAI_API_KEY and SPIKE_ACCESS_TOKEN environment variables are required")
}
// Create OpenAI client
client := openai.NewClient(option.WithAPIKey(openaiKey))
// Configure the SpikeAI MCP tool
mcpTool := responses.ToolUnionParam{
OfMcp: &responses.ToolMcpParam{
Type: "mcp",
ServerLabel: "spike-health-data",
ServerURL: "https://app-api.spikeapi.com/v3/mcp",
Headers: map[string]string{
"Authorization": fmt.Sprintf("Bearer %s", spikeToken),
},
ServerDescription: openai.String("Health and fitness data analysis server"),
RequireApproval: responses.ToolMcpRequireApprovalUnionParam{
OfMcpToolApprovalSetting: openai.String("never"),
},
},
}
// Create the chat request
request := responses.ResponseNewParams{
Model: responses.ChatModelO4Mini,
Instructions: openai.String("You are a health data analyst. Use the available tools to analyze the user's health data and provide insights."),
Input: responses.ResponseNewParamsInputUnion{
OfString: openai.String("Analyze my sleep data for the past 3 days and give me a brief summary of my sleep patterns."),
},
Tools: []responses.ToolUnionParam{mcpTool},
ToolChoice: responses.ResponseNewParamsToolChoiceUnion{
OfToolChoiceMode: openai.Opt(responses.ToolChoiceOptionsAuto),
},
MaxOutputTokens: openai.Opt(int64(1000)),
MaxToolCalls: openai.Opt(int64(3)),
ParallelToolCalls: openai.Opt(true),
}
// Make the API call
ctx := context.Background()
response, err := client.Responses.New(ctx, request)
if err != nil {
log.Fatalf("API call failed: %v", err)
}
// Print the response
fmt.Println("=== Health Data Analysis ===")
fmt.Println(response.OutputText())
// Print usage statistics
fmt.Printf("\nTokens used - Input: %d, Output: %d, Total: %d\n",
response.Usage.InputTokens,
response.Usage.OutputTokens,
response.Usage.TotalTokens)
}
```
```python [Python] theme={null}
import os
import sys
from openai import OpenAI
def main():
# Required environment variables
openai_key = os.getenv("OPENAI_API_KEY")
spike_token = os.getenv("SPIKE_ACCESS_TOKEN")
if not openai_key or not spike_token:
print("Error: OPENAI_API_KEY and SPIKE_ACCESS_TOKEN environment variables are required")
sys.exit(1)
# Create OpenAI client
client = OpenAI(api_key=openai_key)
# Configure the SpikeAI MCP tool
mcp_tool = {
"type": "mcp",
"server_label": "spike-health-data",
"server_url": "https://app-api.spikeapi.com/v3/mcp",
"headers": {
"Authorization": f"Bearer {spike_token}"
},
"server_description": "Health and fitness data analysis server",
"require_approval": "never"
}
try:
# Create the responses request
response = client.responses.create(
model="gpt-4o",
input=[
{
"role": "user",
"content": "Analyze my sleep data for the past 3 days and give me a brief summary of my sleep patterns."
}
],
instructions="You are a health data analyst. Use the available tools to analyze the user's health data and provide insights.",
tools=[mcp_tool],
max_tokens=1000
)
# Print the response
print("=== Health Data Analysis ===")
print(response.output[0].content)
# Print usage statistics if available
if hasattr(response, 'usage'):
usage = response.usage
print(f"\nTokens used - Input: {usage.prompt_tokens}, Output: {usage.completion_tokens}, Total: {usage.total_tokens}")
except Exception as e:
print(f"API call failed: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
```
```typescript [Node.js] theme={null}
const openaiKey = process.env.OPENAI_API_KEY;
const spikeToken = process.env.SPIKE_ACCESS_TOKEN;
if (!openaiKey || !spikeToken) {
console.error('Error: OPENAI_API_KEY and SPIKE_ACCESS_TOKEN environment variables are required');
process.exit(1);
}
// Create OpenAI client
const client = new OpenAI({
apiKey: openaiKey
});
// Configure the SpikeAI MCP tool
const mcpTool = {
type: 'mcp',
server_label: 'spike-health-data',
server_url: 'https://app-api.spikeapi.com/v3/mcp',
headers: {
'Authorization': `Bearer ${spikeToken}`
},
server_description: 'Health and fitness data analysis server',
require_approval: 'never'
};
try {
// Create the response request using the new responses API
const response = await client.responses.create({
model: 'o1-mini',
instructions: 'You are a health data analyst. Use the available tools to analyze the user\'s health data and provide insights.',
input: 'Analyze my sleep data for the past 3 days and give me a brief summary of my sleep patterns.',
tools: [mcpTool],
tool_choice: 'auto',
max_output_tokens: 1000,
max_tool_calls: 3,
parallel_tool_calls: true
});
// Print the response
console.log('=== Health Data Analysis ===');
console.log(response.output_text);
// Print usage statistics
const usage = response.usage;
console.log(`\nTokens used - Input: ${usage.input_tokens}, Output: ${usage.output_tokens}, Total: ${usage.total_tokens}`);
} catch (error) {
console.error(`API call failed: ${error.message}`);
process.exit(1);
}
```
## Anthropic
```bash [Curl] theme={null}
curl https://api.anthropic.com/v1/messages \
-H "Content-Type: application/json" \
-H "X-API-Key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-04-04" \
-d '{
"model": "claude-sonnet-4-20250514",
"max_tokens": 1024,
"mcp_servers": [
{
"type": "url",
"name": "spike-health-data",
"url": "https://app-api.spikeapi.com/v3/mcp",
"authorization_token": "SPIKE_ACCESS_TOKEN",
"tool_configuration": {
"enabled": true,
}
}
],
"messages": [
{
"role": "user",
"content": "Analyze my health data for 2025-08-17 and provide a summary of my health data."
}
]
}'
```
```python [Python] theme={null}
import os
import sys
from anthropic import Anthropic
def main():
# Required environment variables
anthropic_key = os.getenv("ANTHROPIC_API_KEY")
spike_token = os.getenv("SPIKE_ACCESS_TOKEN")
if not anthropic_key or not spike_token:
print("Error: ANTHROPIC_API_KEY and SPIKE_ACCESS_TOKEN environment variables are required")
sys.exit(1)
# Create Anthropic client
client = Anthropic(api_key=anthropic_key)
try:
# Create the message request using MCP server
response = client.beta.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
messages=[
{
"role": "user",
"content": "You are a health data analyst. Analyze my sleep data for the past 3 days and give me a brief summary of my sleep patterns.",
}
],
mcp_servers=[
{
"type": "url",
"url": "https://app-api.spikeapi.com/v3/mcp",
"authorization_token": spike_token,
"name": "spike-health-data",
"tool_configuration": {
"enabled": True,
},
}
],
extra_headers={
"anthropic-beta": "mcp-client-2025-04-04",
},
)
# Print the response
print("=== Health Data Analysis ===")
for content_block in response.content:
if content_block.type == "text":
print(content_block.text)
# Print usage statistics
usage = response.usage
print(f"\nTokens used - Input: {usage.input_tokens}, Output: {usage.output_tokens}")
except Exception as e:
print(f"API call failed: {e}")
sys.exit(1)
if __name__ == "__main__":
main()
```
# MCP Overview
Source: https://docs.spikeapi.com/mcp-docs/overview
Overview of the Spike MCP server and how to use it
## What is MCP?
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/docs/getting-started/intro) is an open standard that enables AI applications to securely connect to external data sources and tools. Think of it as a bridge that allows AI models like ChatGPT, Claude, or your custom AI application to access real-world data and perform actions on your behalf.
## Spike's MCP Server
Spike provides a ready-to-use MCP server that makes health and fitness data accessible to AI applications. This means you can ask AI assistants natural language questions about your health data, and they can automatically fetch, analyze, and interpret information from your connected wearables and health devices. The MCP server handles authentication, data retrieval, and formatting, so AI applications can focus on analysis and user interaction rather than API integration complexity.
For more information about how to use the MCP server, see the [Implementation Guide](/mcp-docs/implementation).
For a list of available MCP tools, see the [Tools](/mcp-docs/tools) page.
## Common Use Cases
### Personal Health Analysis
Ask AI assistants to analyze your health trends and provide insights:
* *"How has my sleep quality changed over the past month?"*
* *"Compare my activity levels between weekdays and weekends"*
* *"What patterns do you see in my heart rate data?"*
### Health Coaching & Recommendations
Get personalized recommendations based on your actual data:
* *"Based on my recent sleep patterns, what should I focus on to improve my rest?"*
* *"Suggest workout adjustments based on my recovery metrics"*
* *"How can I improve my daily step count given my current trends?"*
### Data Correlation & Discovery
Uncover relationships between different health metrics:
* *"Is there a correlation between my sleep duration and next-day activity levels?"*
* *"How does my heart rate variability relate to my stress levels?"*
* *"What impact does my workout intensity have on my recovery time?"*
### Health Reporting & Summaries
Generate comprehensive health reports automatically:
* *"Create a weekly health summary for my doctor's appointment"*
* *"Summarize my fitness progress over the last quarter"*
* *"Generate a sleep quality report for the past two weeks"*
### User Dataset Integration
Combine your own datasets with health metrics for comprehensive analysis:
* *"Analyze my symptom diary alongside my heart rate variability and stress levels"*
* *"How do my mood entries correlate with my sleep quality and activity levels?"*
* *"Compare my productivity journal with my energy levels and recovery metrics"*
* *"Identify patterns between my medication timing and heart rate data"*
### Personalized Health Research
Conduct your own health studies using combined data sources:
* *"Track how my meditation practice affects my HRV and resting heart rate"*
* *"Analyze the relationship between my diet log and my sleep duration"*
* *"How does my work stress level (from my journal) impact my recovery score?"*
* *"Find correlations between my supplement intake and energy levels"*
### Application Integration
Build AI-powered features into your health applications:
* Automated health insights in mobile apps
* Personalized coaching recommendations
* Natural language health data queries
* Intelligent health trend analysis
* User-provided dataset correlation analysis
# MCP Tools
Source: https://docs.spikeapi.com/mcp-docs/tools
Tools available in the MCP server
## Available Tools
The MCP server provides access to all Spike API endpoints including sleep data, daily statistics, workouts, and user profiles. Tools are automatically discovered and made available to the AI model. However, you can also manually limit the tools that are available to the AI model for security and optimization purposes
## Tools
The MCP server provides the following tools:
### `query_statistics_hourly`
Query hourly health and fitness statistics aggregated by hour intervals. Returns hourly aggregated values for activity patterns like steps, distance, calories, heart rate, and sleep duration.
### `query_statistics_daily`
Query daily health and fitness statistics aggregated by day. Returns daily aggregated values for health metrics including steps, distance, calories, heart rate, sleep data, and wellness scores.
### `query_sleep`
Query sleep data for a specific day. Returns detailed sleep information including sleep stages and duration for the requested date.
### `query_workout`
Query workout data for a specific date range. Returns comprehensive workout information with optional detailed breakdowns including sessions, laps, GPS routes, splits, and segments.
### `get_user_info`
Get user information including application details, user ID, and integrated providers. Returns account overview and connected health data provider status.
### `get_user_properties`
Get user properties including personal health and demographic information. Returns profile data such as timezone, physical measurements, and demographics collected from connected devices.
# Nutrition AI Asynchronous Processing
Source: https://docs.spikeapi.com/nutrition-ai/async
Handle nutrition analysis with asynchronous processing and real-time webhook notifications
## Asynchronous Processing Overview
```mermaid theme={null}
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#ffffff", "primaryTextColor": "#161a1d", "primaryBorderColor": "#ded1e4", "lineColor": "#006ad4", "secondaryColor": "#f2f6f9", "tertiaryColor": "#eef1f4", "background": "#ffffff", "mainBkg": "#ffffff", "secondBkg": "#f2f6f9", "actorBkg": "#eef1f4", "actorTextColor": "#161a1d", "actorLineColor": "#006ad4", "signalColor": "#006ad4", "signalTextColor": "#161a1d", "labelBoxBkgColor": "#ffffff", "labelTextColor": "#4f5356", "loopTextColor": "#4f5356", "noteBkgColor": "#f2f6f9", "noteTextColor": "#4f5356", "activationBkgColor": "#ded1e4", "activationBorderColor": "#9ea1a5"}}}%%
sequenceDiagram
participant A as App
participant S as Spike API
A->>S: POST /auth/hmac
HMAC signature
activate S
S-->>A: Access Token
deactivate S
A->>S: POST /nutrition_records
Base64 image + token (wait_on_process: false)
activate S
activate S
S-->>A: 200 OK
record_id, status: "pending"
deactivate S
S->>+A: POST webhook URL
Complete nutrition results
deactivate S
A-->>-S: 200 OK
```
Asynchronous processing allows you to upload food images and receive immediate responses while the AI analysis happens in the background. This approach provides better user experience for interactive applications and enables you to handle multiple requests efficiently.
## How Asynchronous Processing Works
1. **Upload Image** — send a POST request with `wait_on_process: false` (default)
2. **Immediate Response** — receive a response with `status: "pending"` or `status: "processing"`, and a `record_id`
3. **Background Processing** — AI models analyze the image (typically 4–30 seconds)
4. **Get Results** — receive webhook notification with an analysis report when finished
## Getting Results
### Webhooks
Configure a webhook URL to receive real-time notifications when analysis completes:
**Request:**
```json theme={null}
{
"body": "base64-encoded-image-data",
"include_ingredients": true,
"include_nutri_score": true,
"wait_on_process": false
}
```
**Immediate Response:**
```json theme={null}
{
"record_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"status": "processing",
"uploaded_at": "2025-09-15T10:30:04.521Z"
}
```
**Webhook Notification** (when analysis completes):
```json theme={null}
{
"application_user_id": "end_user_id1",
"record_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"status": "completed",
"dish_name": "grilled chicken caesar salad",
"dish_description": "chicken breast, grilled with romaine lettuce, parmesan cheese, and caesar dressing",
"nutri_score": "B",
"serving_size": 275,
"unit": "g",
"nutritional_fields": {
"energy_kcal": 292,
"protein_g": 42.2,
"fat_total_g": 10.9,
"carbohydrate_g": 4.3,
"fiber_total_dietary_g": 2.1
},
"ingredients": [
{
"name": "chicken breast, grilled",
"serving_size": 150.0,
"unit": "g",
"nutritional_fields": {
"energy_kcal": 165,
"protein_g": 31,
"fat_total_g": 3.6,
"carbohydrate_g": 0,
"fiber_total_dietary_g": 0
}
}
],
"uploaded_at": "2025-09-15T10:30:04.521Z",
"modified_at": "2025-09-15T10:30:12.132Z",
"consumed_at": "2025-09-15T10:30:04Z"
}
```
### Alternative: Manual Checking
If webhooks are not available in your environment, you can check the status using the [`GET /nutrition_records/{id}`](/api-reference/nutrition-ai-get-nutrition-record) endpoint:
```
GET /nutrition_records/6ba7b810-9dad-11d1-80b4-00c04fd430c8
```
## Webhook Configuration
### Setup
Configure your webhook URL through the [admin console](https://admin.spikeapi.com/). Your endpoint must:
* Respond with HTTP 200 to acknowledge receipt
* Handle POST requests with JSON payloads
* Verify HMAC signatures for security
If requests fail, the system retries **up to 10 times** with exponential backoff.
### Security
All webhook requests include HMAC SHA256 signature verification:
* **Header** — `x-body-signature` contains the hex-encoded HMAC signature
* **Key** — uses your application's webhook signature key (configured in the admin console)
* **Algorithm** — `HMAC-SHA256(webhook_signature_key, request_body)`
See [Webhook Signature](/api-docs/webhooks) for more details.
### Status Handling
Your webhook handler should handle different status values. For complete status definitions, see [Processing Status](/nutrition-ai/implementation#processing-status).
When `status` is `failed`, check the `failure_reason` field for specific details about what prevented successful analysis.
## Best Practices
### 1. Webhook Implementation
* **Respond quickly** — always respond with HTTP 200 immediately, then process data asynchronously
* **Validate signatures** — always verify the HMAC signature before processing webhook data
* **Handle failures gracefully** — check the status field and handle both success and failure cases
* **Implement idempotency** — use the `record_id` to avoid processing the same webhook multiple times
### 2. Error Handling
* Handle webhook delivery failures gracefully
* Store webhook secrets securely and never commit them to version control
* Implement retry logic for critical webhook processing
### 3. User Experience
* Show immediate feedback when the image is uploaded ("Analysis in progress...")
* Provide loading indicators during the processing window
* Handle both success and failure states in your UI
## Use Cases
Asynchronous processing is ideal for:
* **Mobile applications** — immediate feedback while processing happens in the background
* **Web applications** — non-blocking user interfaces with real-time updates
* **High-volume scenarios** — process multiple images concurrently
* **User-facing tools** — food logging apps, dietary tracking applications
## Implementation Examples
```go [Go] theme={null}
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
const webhookSignatureKey = "YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE"
// NutritionRecord represents the webhook payload structure
type NutritionRecord struct {
ApplicationID int64 `json:"application_id"`
UID string `json:"uid"`
RecordID string `json:"record_id"`
Status string `json:"status"`
DishName string `json:"dish_name"`
NutriScore string `json:"nutri_score"`
UploadedAt time.Time `json:"uploaded_at"`
ModifiedAt time.Time `json:"modified_at"`
// Add other fields as needed
}
func main() {
http.HandleFunc("/nutrition-webhook", func(w http.ResponseWriter, r *http.Request) {
// Verify HMAC signature
signature := r.Header.Get("x-body-signature")
if signature == "" {
http.Error(w, "Missing signature", http.StatusBadRequest)
return
}
// Read the request body
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read body", http.StatusInternalServerError)
return
}
// Calculate and verify HMAC
hm := hmac.New(sha256.New, []byte(webhookSignatureKey))
hm.Write(body)
expectedSignature := hex.EncodeToString(hm.Sum(nil))
if signature != expectedSignature {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
// Parse nutrition record
var record NutritionRecord
if err := json.Unmarshal(body, &record); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Process the nutrition record
fmt.Printf("Received nutrition analysis: %s for user %s\n", record.Status, record.UID)
if record.Status == "completed" {
fmt.Printf("Dish: %s, Nutri-Score: %s\n", record.DishName, record.NutriScore)
// Update your application with the results
} else if record.Status == "failed" {
fmt.Printf("Analysis failed for record %s\n", record.RecordID)
// Handle failure case
}
// Respond with success
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
fmt.Println("Starting nutrition webhook server on port 8000")
http.ListenAndServe(":8000", nil)
}
```
```javascript [JavaScript] theme={null}
const crypto = require("crypto");
const express = require("express");
const WEBHOOK_SIGNATURE_KEY = "YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE";
const app = express();
// Middleware to capture raw body for signature verification
app.use('/nutrition-webhook', express.raw({type: 'application/json'}));
app.post('/nutrition-webhook', (req, res) => {
const signature = req.headers['x-body-signature'];
if (!signature) {
return res.status(400).send('Missing signature');
}
// Verify HMAC signature
const hmac = crypto.createHmac('sha256', WEBHOOK_SIGNATURE_KEY);
hmac.update(req.body);
const expectedSignature = hmac.digest('hex');
if (signature !== expectedSignature) {
return res.status(401).send('Invalid signature');
}
// Parse nutrition record
try {
const nutritionRecord = JSON.parse(req.body);
console.log(`Received nutrition analysis: ${nutritionRecord.status} for user ${nutritionRecord.uid}`);
if (nutritionRecord.status === 'completed') {
console.log(`Dish: ${nutritionRecord.dish_name}, Nutri-Score: ${nutritionRecord.nutri_score}`);
// Update your application with the results
updateUserNutritionData(nutritionRecord);
} else if (nutritionRecord.status === 'failed') {
console.log(`Analysis failed: ${nutritionRecord.failure_reason}`);
// Handle failure case
handleAnalysisFailure(nutritionRecord.record_id, nutritionRecord.uid);
}
res.status(200).send('OK');
} catch (error) {
res.status(400).send('Invalid JSON');
}
});
function updateUserNutritionData(nutritionRecord) {
// Your application logic to store/process the nutrition data
console.log('Updating user nutrition data...');
}
function handleAnalysisFailure(recordId, uid) {
// Your application logic to handle failed analysis
console.log('Handling analysis failure...');
}
app.listen(8000, () => {
console.log('Nutrition webhook server running on port 8000');
});
```
```python [Python] theme={null}
import hmac
import hashlib
import json
from datetime import datetime
from flask import Flask, request, Response
app = Flask(__name__)
WEBHOOK_SIGNATURE_KEY = "YOUR_WEBHOOK_SECRET_FROM_ADMIN_CONSOLE"
def verify_signature(body: bytes, signature: str) -> bool:
"""Verify the HMAC signature of the request body."""
expected_signature = hmac.new(
WEBHOOK_SIGNATURE_KEY.encode('utf-8'),
body,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
@app.route('/nutrition-webhook', methods=['POST'])
def handle_nutrition_webhook():
# Get signature from headers
signature = request.headers.get('x-body-signature')
if not signature:
return Response('Missing signature', status=400)
# Get raw request body
body = request.get_data()
# Verify signature
if not verify_signature(body, signature):
return Response('Invalid signature', status=401)
# Parse nutrition record
try:
nutrition_record = json.loads(body)
print(f"Received nutrition analysis: {nutrition_record['status']} for user {nutrition_record['uid']}")
if nutrition_record['status'] == 'completed':
print(f"Dish: {nutrition_record.get('dish_name', 'Unknown')}")
print(f"Nutri-Score: {nutrition_record.get('nutri_score', 'N/A')}")
# Update your application with the results
update_user_nutrition_data(nutrition_record)
elif nutrition_record['status'] == 'failed':
print(f"Analysis failed: {nutrition_record.get('failure_reason', 'Unknown error')}")
# Handle failure case
handle_analysis_failure(nutrition_record['record_id'], nutrition_record['uid'])
return Response('OK', status=200)
except json.JSONDecodeError:
return Response('Invalid JSON', status=400)
def update_user_nutrition_data(nutrition_record):
"""Update your application with the nutrition analysis results."""
print("Updating user nutrition data...")
# Your application logic here
def handle_analysis_failure(record_id, uid):
"""Handle failed nutrition analysis."""
print("Handling analysis failure...")
# Your application logic here
if __name__ == '__main__':
print('Starting nutrition webhook server on port 8000')
app.run(host='0.0.0.0', port=8000)
```
```php [PHP] theme={null}
updateUserNutritionData($nutritionRecord);
} elseif ($nutritionRecord['status'] === 'failed') {
$failureReason = $nutritionRecord['failure_reason'] ?? 'Unknown error';
echo "Analysis failed: {$failureReason}\n";
// Handle failure case
$this->handleAnalysisFailure($nutritionRecord['record_id'], $nutritionRecord['uid']);
}
http_response_code(200);
echo 'OK';
}
private function updateUserNutritionData($nutritionRecord) {
// Your application logic to store/process the nutrition data
echo "Updating user nutrition data...\n";
}
private function handleAnalysisFailure($recordId, $uid) {
// Your application logic to handle failed analysis
echo "Handling analysis failure...\n";
}
}
// Handle the webhook request
$webhook = new NutritionWebhook();
$webhook->handleRequest();
?>
```
For complete API specifications and additional configuration options, see the [Implementation Guide](/nutrition-ai/implementation).
# Nutrition AI Implementation Guide
Source: https://docs.spikeapi.com/nutrition-ai/implementation
Complete code examples and integration patterns for the Nutrition API
## About
This guide provides complete implementation details and best practices for integrating the Nutrition API into your applications. For detailed API specifications and data types, refer to the [API Reference](/api-reference/nutrition-ai-analyze-nutrition-image).
**Authentication**: The code examples in this guide focus on the nutrition analysis functionality. For authentication implementation details, see the [Authentication documentation](/api-docs/authentication). You'll need to add Bearer token into the Authorization header to all API requests.
## API Endpoints
The Nutrition API provides these endpoints for managing nutrition records:
* **[`POST /nutrition_records/image`](/api-reference/nutrition-ai-analyze-nutrition-image)** — upload a food image for AI-powered nutritional analysis
* **[`POST /nutrition_records/ingredients/label`](/api-reference/nutrition-ai-analyze-nutrition-label)** — upload an image of a nutritional facts label to analyze it
* **[`POST /nutrition_records/manual`](/api-reference/nutrition-ai-upload-nutrition-record)** — upload a manually created nutrition record
* **[`PATCH /nutrition_records/{id}`](/api-reference/nutrition-ai-modify-nutrition-record)** — change the portion size for a nutrition record by ID
* **[`PUT /nutrition_records/{id}`](/api-reference/nutrition-ai-replace-nutrition-record)** — replace the nutrition record with a new one
* **[`GET /nutrition_records`](/api-reference/nutrition-ai-list-nutrition-records)** — retrieve a list of nutrition records by the time range
* **[`GET /nutrition_records/{id}`](/api-reference/nutrition-ai-get-nutrition-record)** — retrieve a specific nutrition record by ID
* **[`DELETE /nutrition_records/{id}`](/api-reference/nutrition-ai-delete-nutrition-record)** — delete a specific nutrition record by ID
## Image Preparation
For optimal image capture guidelines, see the [Image Guidelines](/nutrition-ai/overview#image-guidelines) in the API overview.
## POST Request Body
### Processing Modes
The processing mode is controlled by the `wait_on_process` parameter. The API supports two processing modes, each suited for a different use case:
#### Asynchronous Processing
Returns immediately and processes the image in the background. Ideal for user-facing applications where immediate feedback is important. See the [Asynchronous Processing](/nutrition-ai/async) guide for complete implementation details, webhook configuration, and code examples.
#### Synchronous Processing
```mermaid theme={null}
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#ffffff", "primaryTextColor": "#161a1d", "primaryBorderColor": "#ded1e4", "lineColor": "#006ad4", "secondaryColor": "#f2f6f9", "tertiaryColor": "#eef1f4", "background": "#ffffff", "mainBkg": "#ffffff", "secondBkg": "#f2f6f9", "actorBkg": "#eef1f4", "actorTextColor": "#161a1d", "actorLineColor": "#006ad4", "signalColor": "#006ad4", "signalTextColor": "#161a1d", "labelBoxBkgColor": "#ffffff", "labelTextColor": "#4f5356", "loopTextColor": "#4f5356", "noteBkgColor": "#f2f6f9", "noteTextColor": "#4f5356", "activationBkgColor": "#ded1e4", "activationBorderColor": "#9ea1a5"}}}%%
sequenceDiagram
participant A as App
participant S as Spike API
A->>+S: POST /auth/hmac
HMAC signature
S-->>-A: Access Token
A->>+S: POST /nutrition_records
Base64 image + token (wait_on_process: true)
activate S
S-->>A: 200 OK
Complete nutrition analysis with status: "completed"
deactivate S
S->>-A: POST webhook URL
Complete nutrition results
activate A
A-->>+S: 200 OK
deactivate A
```
Waits for complete analysis before responding. Best for batch processing, server-to-server integrations, or when you can handle longer response times:
**Request:**
```json theme={null}
{
"body": "base64-encoded-image-data",
"analysis_mode": "fast",
"wait_on_process": true
}
```
**Response:**
```json theme={null}
{
"record_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"status": "completed",
"dish_name": "grilled chicken caesar salad",
"serving_size": 275,
"unit": "g",
"nutritional_fields": {
"energy_kcal": 292,
"protein_g": 42.2,
"fat_total_g": 10.9,
"carbohydrate_g": 4.3
},
"uploaded_at": "2025-09-15T10:30:04.521Z",
"modified_at": "2025-09-15T10:30:12.132Z",
"consumed_at": "2025-09-15T10:30:04Z"
}
```
### Analysis Modes
* **`precise`** (default) — uses advanced AI models for the highest accuracy and detailed analysis
* **`fast`** — uses optimized models for quicker processing with good accuracy
### Localization
Provide `country_code` and `language_code` for region-specific analysis:
```json theme={null}
{
"country_code": "de",
"language_code": "de",
"include_dish_description": true
}
```
Note, both codes should be lowercase.
This enables:
* Region-specific food recognition
* Usage of local nutritional databases
* Translated ingredient names and descriptions
```json Request Body theme={null}
{
"include_dish_description": true,
"include_ingredients": true,
"wait_on_process": true,
"country_code": "fr",
"language_code": "fr",
"body_url": "https://images.ricardocuisine.com/services/recipes/992x1340_9168.jpg"
}
```
```json Response Body theme={null}
{
"record_id": "013501f8-da8f-88ed-8ef8-a21ad5d0e7bf",
"status": "completed",
"dish_name": "french omelette with mixed salad",
"dish_description": "a classic French omelette made with whisked eggs, gently cooked for a soft texture, garnished with chives, served with a side of mixed green salad and a slice of toasted bread",
"dish_name_translated": "Omelette française avec salade composée",
"dish_description_translated": "Une omelette française classique, préparée avec des œufs battus, cuite doucement pour une texture moelleuse, garnie de ciboulette, servie avec une salade verte composée et une tranche de pain grillé.",
"serving_size": 197,
"unit": "g",
"nutritional_fields": {
"carbohydrate_g": 18.3,
"energy_kcal": 288,
"fat_total_g": 15.4,
"protein_g": 17.3
},
"ingredients": [
{
"name": "egg, whole, cooked, omelette",
"name_translated": "œuf entier cuit en omelette",
"serving_size": 120,
"unit": "g",
"nutritional_fields": {
"carbohydrate_g": 2.4,
"energy_kcal": 185,
"fat_total_g": 14.1,
"protein_g": 13.3
}
},
{
"name": "chives, raw",
"name_translated": "ciboulette crue",
"serving_size": 2,
"unit": "g",
"nutritional_fields": {
"carbohydrate_g": 0.2,
"energy_kcal": 1,
"protein_g": 0.1
}
},
{
"name": "lettuce, mixed salad greens, raw",
"name_translated": "laitue, salades composées, crues",
"serving_size": 40,
"unit": "g",
"nutritional_fields": {
"carbohydrate_g": 0.7,
"energy_kcal": 7,
"fat_total_g": 0.1,
"protein_g": 0.8
}
},
{
"name": "bread, white, toasted",
"name_translated": "pain blanc grillé",
"serving_size": 35,
"unit": "g",
"nutritional_fields": {
"carbohydrate_g": 15,
"energy_kcal": 95,
"fat_total_g": 1.2,
"protein_g": 3.1
}
}
],
"uploaded_at": "2025-09-15T12:51:41.886Z",
"modified_at": "2025-09-15T12:51:49.861Z",
"consumed_at": "2025-09-15T12:51:41Z"
}
```
### Including Optional Data
Control what data is included in the analysis:
```json theme={null}
{
"include_nutri_score": true,
"include_dish_description": true,
"include_ingredients": true,
"include_nutrition_fields": [
"energy_kcal",
"protein_g",
"fat_total_g",
"carbohydrate_g",
"fiber_total_dietary_g",
"sodium_mg",
"calcium_mg",
"iron_mg"
]
}
```
#### Selection of Nutritional Fields
You can control which nutritional fields are included in the analysis:
```json theme={null}
{
"include_nutrition_fields": [
"energy_kcal",
"protein_g",
"fat_total_g",
"carbohydrate_g",
"fiber_total_dietary_g",
"sodium_mg",
"calcium_mg",
"vitamin_c_mg"
]
}
```
**Note**: If `include_nutrition_fields` is omitted or empty, only four basic fields are included by default:
* `carbohydrate_g`
* `energy_kcal`
* `fat_total_g`
* `protein_g`
## Response Body
### Processing Status
Analysis of the nutrition record progresses through these states:
* **pending** — analysis has been queued
* **processing** — an AI model is actively analyzing the image
* **completed** — analysis finished successfully with results
* **failed** — processing failed due to unidentifiable content or technical issues
* **updated** — an existing record was updated with a new serving size or fully replaced
REST API responses and webhook notifications will always include the `status` field.
```json theme={null}
{
"record_id": "0135012d-a9f5-8bf6-885f-8badcaf8d203",
"status": "processing",
"uploaded_at": "2025-09-15T20:02:01.049Z",
"modified_at": "2025-09-15T20:02:01.05Z",
"consumed_at": "2025-09-15T20:02:01Z"
}
```
Webhook notifications can have `status` field with values `completed`, `updated` or `failed`. Always handle different response statuses in your application logic.
### Error Scenarios
#### Processing Failures
These occur when the API successfully receives your request but the AI analysis fails. The response will have HTTP 200 status with `"status": "failed"`.
When `status` is `failed`, check the `failure_reason` field for specific details:
* **Unidentifiable food items** — image contains non-food objects or unclear food items
```json theme={null}
"status": "failed",
"dish_name": "unidentifiable",
"failure_reason": "no food items visible; image depicts landmark (Eiffel Tower)",
```
* **Poor image quality** — blurry, dark, or low-resolution images
```json theme={null}
"status": "failed",
"dish_name": "unidentifiable",
"failure_reason": "image too blurry to identify any food items clearly",
```
* **AI processing timeouts** — although AI providers typically have fast [Response Times](/nutrition-ai/overview#response-times), processing may occasionally take longer than expected due to reasons that cannot be foreseen.
To manage this, the upload process will time out if synchronous processing takes longer than 3 minutes and asynchronous processing takes longer than 5 minutes.
Once timeout occurs, the API will return HTTP status 200 and the body message with the `status` field set to `failed`:
```json theme={null}
{
"record_id": "013501f7-1ad0-8c25-82ae-fc843ead4135",
"status": "failed",
"failure_reason": "nutrition processing timed out",
"uploaded_at": "2025-09-15T20:48:19.731Z",
"modified_at": "2025-09-15T20:51:04.815Z",
"consumed_at": "2025-09-15T20:48:19Z"
}
```
#### Request Errors
These occur when there's an issue with the request itself, returning non-200 HTTP status codes before analysis begins.
* **Decoding Image Error** — if the image is corrupted or improperly encoded, and therefore cannot be decoded, then HTTP 400 will be returned.
```json theme={null}
{
"title": "Bad Request",
"status": 400,
"detail": "failed to base64 decode body"
}
```
* **Invalid Image Format** — nutrition AI supports JPEG, PNG, and WebP formats. When an image format is not supported, HTTP status code 400 will be returned.
```json theme={null}
{
"title": "Bad Request",
"status": 400,
"detail": "unsupported mime type: image/gif"
}
```
* **Exceeding Image Size Limits** — images larger than 10MB are not accepted, and API will return HTTP 413 status code.
```json theme={null}
{
"title": "Request Entity Too Large",
"status": 413,
"detail": "request body is too large limit=10485760 bytes"
}
```
Images larger than 11MB will be rejected by the gateway with HTTP 413 status code and the following error message.
```text theme={null}
413 Request Entity Too Large
```
* **Invalid Image URLs** — when image is provided with the `body_url` parameter and the URL path is not found at the host, HTTP status code 422 will be returned.
```json theme={null}
{
"title": "Unprocessable Entity",
"status": 422,
"detail": "failed to GET body_url: status 404"
}
```
If the `body_url` parameter value is not a proper HTTP(S) URL, the following error will be given.
```json theme={null}
{
"title": "Unprocessable Entity",
"status": 422,
"detail": "validation failed",
"errors": [
{
"message": "body_url must be a valid HTTP(S) URL",
"location": "body.body_url",
"value": "example.com/image.jpeg"
}
]
}
```
For general API error handling, see the [Error Handling documentation](/api-docs/errors).
### Nutrition Fields and Units
The names of nutrition fields are always lowercase and include units as suffix. Nutrition fields are not translated.
## Custom Nutrition Records
A full set of Nutrition AI endpoints allows sophisticated users to define their own nutrition records flexibly and precisely.
Extracting nutrition details from nutrition facts labels lets users build up their own nutrition records from multiple ingredients.
This is useful when users have existing nutritional data, follow recipes, or want to add custom items to the analyzed nutrition records.
### Creating Nutrition Records Manually
[`POST /nutrition_records/manual`](/api-reference/nutrition-ai-upload-nutrition-record) endpoint allows you to define a full nutrition record with all ingredients and their respective nutritional fields:
* Dish name and description including translation
* Serving size and unit
* Complete nutritional fields
* Individual ingredients with their nutritional breakdown
* Consumption timestamp
### Updating Nutrition Records
Once a nutrition record exists (whether created manually or via AI analysis), you can update it in several ways:
#### 1. Changing Serving Size
Use [`PATCH /nutrition_records/{id}`](/api-reference/nutrition-ai-modify-nutrition-record) endpoint to adjust the portion size while maintaining the same nutritional ratios.
All ingredients and their respective nutritional fields will be automatically recalculated proportionally.
This allows you to easily adjust portion sizes while maintaining accurate nutritional information.
#### 2. Updating the Consumption Time
Use [`PATCH /nutrition_records/{id}`](/api-reference/nutrition-ai-modify-nutrition-record) endpoint to add or update the time of consumption.
#### 3. Replacing Full Record
Use [`PUT /nutrition_records/{id}`](/api-reference/nutrition-ai-replace-nutrition-record) to completely replace a record with new data, including different ingredients and nutritional values.
A response will include an `input_type` field with value `manual` to indicate that the record was fully replaced.
The record preserves the record ID only. The `consumed_at` and `user_time_offset_minutes` field values are updated
either with newly provided values or set to defaults — the same as for a new record.
### Getting Results
For asynchronous processing, use webhooks for real-time notifications. See the [Asynchronous Processing Guide](/nutrition-ai/async) for complete webhook implementation details.
If webhooks are not yet available, you can check status using the [`GET /nutrition_records/{id}`](/api-reference/nutrition-ai-get-nutrition-record) endpoint.
Retrospectively, a list of all analyzed nutrition records can be retrieved using the [`GET /nutrition_records`](/api-reference/nutrition-ai-list-nutrition-records) endpoint by providing the time range.
## Deleting Results
A result can be deleted using [`DETELE /nutrition_records/{id}`](/api-reference/nutrition-ai-delete-nutrition-record) endpoint by providing the record ID.
An HTTP 204 status code will be returned if successful, regardless if the record existed or not.
## Best Practices
### 1. Choose the Right Processing Mode
* **Asynchronous**: For user-facing applications, mobile apps, and when you want immediate feedback
* **Synchronous**: For batch processing, server-to-server integrations, and when you can handle longer response times
### 2. Implement Proper Error Handling
* Handle network errors gracefully
* Provide meaningful error messages to users
* Implement retry logic for transient failures
## Code Examples
Here are complete implementation examples in multiple programming languages:
```python [Python] theme={null}
import os
import requests
import base64
import hmac
import hashlib
import json
SPIKE_API_BASE_URL = os.getenv("SPIKE_API_BASE_URL")
SPIKE_APP_HMAC_KEY = os.getenv("SPIKE_APP_HMAC_KEY")
try:
SPIKE_APP_ID = int(os.getenv("SPIKE_APP_ID"))
except (ValueError, TypeError):
raise ValueError("SPIKE_APP_ID must be an integer.")
# Implementation depends on your authentication method
# See: /api-docs/authentication for complete details
class SpikeAuth:
def __init__(self, end_user_id):
self.user_id = end_user_id
self.app_id = SPIKE_APP_ID
self.hmac_key = SPIKE_APP_HMAC_KEY
self.base_url = SPIKE_API_BASE_URL
self.hmac_signature = self._generate_hmac_signature()
self.access_token = None
def _generate_hmac_signature(self):
h = hmac.new(self.hmac_key.encode('utf-8'), self.user_id.encode('utf-8'), hashlib.sha256)
return h.hexdigest()
def get_bearer_token(self) -> str:
if self.access_token is not None:
return self.access_token
else:
body = {
"application_id": self.app_id,
"application_user_id": self.user_id,
"signature": self.hmac_signature
}
response = requests.post(
f"{self.base_url}/auth/hmac",
json=body,
headers={"Content-Type": "application/json", "Accept": "application/json"},
timeout=20
)
if response.status_code == 200:
try:
print(f"Successfully obtained Bearer token for {self.user_id}")
self.access_token = response.json()["access_token"]
return self.access_token
except (KeyError, json.JSONDecodeError) as e:
print(f"Failed to parse access token from successful response for {self.user_id}: {e}")
raise requests.RequestException(f"Token parsing failed for {self.user_id}")
else:
raise Exception(f"Authentication failed for {self.user_id} with status {response.status_code}")
class NutritionAPI:
def __init__(self, auth : SpikeAuth):
self.spike_auth = auth
def _create_auth_headers(self, method="GET") -> dict[str, str]:
"""Create authentication headers - see authentication docs for details"""
try:
token = self.spike_auth.get_bearer_token()
req_headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json"
}
if method == "POST":
req_headers["Content-Type"] = "application/json"
return req_headers
except requests.RequestException as auth_error:
print(f"Failed to get bearer token for Nutrition API request header: {auth_error}")
raise
def analyze_food_image(self, image_path, **options):
"""Upload a food image for nutritional analysis"""
# Read and encode image
try:
with open(image_path, 'rb') as image_file:
image_data = base64.b64encode(image_file.read()).decode('utf-8')
except FileNotFoundError:
print(f"Image file not found: {image_path}")
return None
# Prepare the request body
body = {
"body": image_data,
"analysis_mode": options.get("analysis_mode", "precise"),
"country_code": options.get("country_code", "us"),
"language_code": options.get("language_code", "en"),
"include_ingredients": options.get("include_ingredients", False),
"include_nutri_score": options.get("include_nutri_score", True),
"include_dish_description": options.get("include_dish_description", True),
"ignore_cache": options.get("ignore_cache", False),
"include_nutrition_fields": options.get("include_nutrition_fields", [
"energy_kcal", "protein_g", "fat_total_g", "carbohydrate_g",
"fiber_total_dietary_g", "sodium_mg"
]),
"wait_on_process": options.get("wait_on_process", False)
}
# Create request body and headers
body_json = json.dumps(body)
headers = self._create_auth_headers(method="POST")
# Make request
response = requests.post(
f"{SPIKE_API_BASE_URL}/nutrition_records",
headers=headers,
data=body_json
)
if response.status_code == 200:
return response.json()
print(f"Response status: {response.status_code}")
if response.status_code == 400:
print(f"Bad request. Response: {response.text}")
elif response.status_code == 401:
print(f"Unauthorized for Nutrition API. Token expired or invalid. Response: {response.text}")
self.spike_auth.access_token = None
elif response.status_code == 404:
print(f"Resource not found")
elif response.status_code == 413:
print(f"Encoded picture exceeds 10MB. Response: {response.text}")
elif response.status_code == 422:
print(f"Invalid parameters. Response: {response.text}")
elif response.status_code == 500:
print(f"Internal server error - contact Spike support. Response: {response.text}")
return None
def print_result(res):
if res is not None:
print(f"Status: {res['status']}. Record ID: {res['record_id']}")
if res['status'] == 'failed':
print(f"Error: {res['failure_reason']}")
elif res['status'] == 'completed':
print(f"Dish: {res['dish_name']}")
if 'dish_name_translated' in res.keys():
print(f"Dish translated: {res['dish_name_translated']}")
if 'dish_description' in res.keys():
print(f"Description: {res['dish_description']}")
if 'dish_description_translated' in res.keys():
print(f"Description translated: {res['dish_description_translated']}")
print(f"Serving size: {res['serving_size']} {res['unit']} ")
if 'nutri_score' in res.keys():
print(f"Nutri-Score: {res['nutri_score']}")
if 'nutritional_fields' in res.keys():
print("Nutritional fields:")
for field in res['nutritional_fields']:
print(f"- {field}: {res['nutritional_fields'].get(field)}")
if 'ingredients' in res.keys():
print("Ingredients:")
for ingredient in res.get('ingredients'):
print(f"- {ingredient['name']}: {ingredient['serving_size']}{ingredient['unit']}")
if 'nutritional_fields' in ingredient.keys():
for field in ingredient['nutritional_fields']:
print(f" + {field}: {ingredient['nutritional_fields'].get(field)}")
# Example usage
user_id = "nutrition_test_user"
spike_auth = SpikeAuth(user_id)
api = NutritionAPI(spike_auth)
# Upload image asynchronously
print(f"Asynchronous analysis")
result = api.analyze_food_image(
"/path/to/image.jpg.jpg",
include_ingredients=True
)
print_result(result)
# For synchronous processing (wait for results)
print(f"\nSynchronous analysis")
result = api.analyze_food_image(
"/path/to/image.jpg.jpg",
wait_on_process=True,
include_ingredients=True,
language_code="de",
country_code="de"
)
print_result(result)
```
```javascript [JavaScript] theme={null}
const crypto = require('crypto');
const fs = require('fs');
const axios = require('axios');
class NutritionAPI {
constructor(apiKey, baseUrl = 'https://api.spikeapi.com') {
this.apiKey = apiKey;
this.baseUrl = baseUrl;
}
createAuthHeaders() {
// Create authentication headers - see /api-docs/authentication for details
// Implementation depends on your authentication method
return {};
}
async uploadFoodImage(imagePath, options = {}) {
// Read and encode image
const imageBuffer = fs.readFileSync(imagePath);
const imageBase64 = imageBuffer.toString('base64');
// Prepare the request body
const body = {
body: imageBase64,
analysis_mode: options.analysisMode || 'precise',
country_code: options.countryCode || 'us',
language_code: options.languageCode || 'en',
include_ingredients: options.includeIngredients !== false,
include_nutri_score: options.includeNutriScore !== false,
include_dish_description: options.includeDishDescription !== false,
include_nutrition_fields: options.includeNutritionFields || [
'energy_kcal', 'protein_g', 'fat_total_g', 'carbohydrate_g',
'fiber_total_dietary_g', 'sodium_mg'
],
wait_on_process: options.waitOnProcess || false
};
// Create request body and headers
const bodyJson = JSON.stringify(body);
const authHeaders = this.createAuthHeaders();
const headers = {
'Content-Type': 'application/json',
...authHeaders
// See /api-docs/authentication for authentication details
};
// Make request
try {
const response = await axios.post(
`${this.baseUrl}/nutrition_records`,
bodyJson,
{ headers }
);
return response.data;
} catch (error) {
throw new Error(`API request failed: ${error.response?.data?.message || error.message}`);
}
}
}
// Example usage
async function analyzeFood() {
const api = new NutritionAPI('your-api-key');
try {
// Asynchronous analysis (recommended)
const result = await api.uploadFoodImage('path/to/food-image.jpg', {
includeIngredients: true,
includeNutriScore: true,
countryCode: 'us'
});
console.log(`Analysis started. Record ID: ${result.record_id}`);
console.log(`Status: ${result.status}`);
// Synchronous analysis (wait for results)
const syncResult = await api.uploadFoodImage('path/to/food-image.jpg', {
waitOnProcess: true,
includeIngredients: true
});
if (syncResult.status === 'completed') {
const nutritionData = syncResult.result;
console.log(`Dish: ${nutritionData.dish_name}`);
console.log(`Nutri-Score: ${nutritionData.nutri_score}`);
nutritionData.ingredients?.forEach(ingredient => {
console.log(`- ${ingredient.name}: ${ingredient.serving_size}${ingredient.unit}`);
});
}
} catch (error) {
console.error('Error:', error.message);
}
}
analyzeFood();
```
```go [Go] theme={null}
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
type NutritionAPI struct {
APIKey string
BaseURL string
}
type UploadRequest struct {
Body string `json:"body"`
AnalysisMode string `json:"analysis_mode"`
CountryCode string `json:"country_code"`
LanguageCode string `json:"language_code"`
IncludeIngredients bool `json:"include_ingredients"`
IncludeNutriScore bool `json:"include_nutri_score"`
IncludeDishDescription bool `json:"include_dish_description"`
IncludeNutritionFields []string `json:"include_nutrition_fields"`
WaitOnProcess bool `json:"wait_on_process"`
}
type UploadResponse struct {
ApplicationID int64 `json:"application_id"`
UID string `json:"uid"`
RecordID string `json:"record_id"`
Status string `json:"status"`
Result *NutritionRecord `json:"result,omitempty"`
Error string `json:"error,omitempty"`
UploadedAt time.Time `json:"uploaded_at"`
}
type NutritionRecord struct {
RecordID string `json:"record_id"`
Status string `json:"status"`
DishName string `json:"dish_name"`
NutriScore string `json:"nutri_score"`
ServingSize float64 `json:"serving_size"`
Unit string `json:"unit"`
NutritionalFields map[string]float64 `json:"nutritional_fields"`
Ingredients []NutritionIngredient `json:"ingredients"`
UploadedAt time.Time `json:"uploaded_at"`
}
type NutritionIngredient struct {
Name string `json:"name"`
ServingSize float64 `json:"serving_size"`
Unit string `json:"unit"`
NutritionalFields map[string]float64 `json:"nutritional_fields"`
}
func NewNutritionAPI(apiKey string) *NutritionAPI {
return &NutritionAPI{
APIKey: apiKey,
BaseURL: "https://api.spikeapi.com",
}
}
func (api *NutritionAPI) createAuthHeaders() map[string]string {
// Create authentication headers - see /api-docs/authentication for details
// Implementation depends on your authentication method
return map[string]string{
"Content-Type": "application/json",
// Add authentication headers here
}
}
func (api *NutritionAPI) UploadFoodImage(imagePath string, options UploadRequest) (*UploadResponse, error) {
// Read and encode image
imageData, err := os.ReadFile(imagePath)
if err != nil {
return nil, fmt.Errorf("failed to read image: %w", err)
}
options.Body = base64.StdEncoding.EncodeToString(imageData)
// Set defaults
if options.AnalysisMode == "" {
options.AnalysisMode = "precise"
}
if options.CountryCode == "" {
options.CountryCode = "us"
}
if options.LanguageCode == "" {
options.LanguageCode = "en"
}
if options.IncludeNutritionFields == nil {
options.IncludeNutritionFields = []string{
"energy_kcal", "protein_g", "fat_total_g", "carbohydrate_g",
"fiber_total_dietary_g", "sodium_mg",
}
}
// Create a request body
bodyBytes, err := json.Marshal(options)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}
// Create request
req, err := http.NewRequest("POST", api.BaseURL+"/nutrition_records", bytes.NewBuffer(bodyBytes))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}
// Set headers
authHeaders := api.createAuthHeaders()
for key, value := range authHeaders {
req.Header.Set(key, value)
}
// See /api-docs/authentication for authentication details
// Make request
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
// Parse response
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err)
}
var result UploadResponse
if err := json.Unmarshal(respBody, &result); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}
return &result, nil
}
func main() {
api := NewNutritionAPI("your-api-key")
// Asynchronous analysis
result, err := api.UploadFoodImage("path/to/food-image.jpg", UploadRequest{
IncludeIngredients: true,
IncludeNutriScore: true,
CountryCode: "us",
})
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("Analysis started. Record ID: %s\n", result.RecordID)
fmt.Printf("Status: %s\n", result.Status)
// Synchronous analysis
syncResult, err := api.UploadFoodImage("path/to/food-image.jpg", UploadRequest{
WaitOnProcess: true,
IncludeIngredients: true,
})
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
if syncResult.Status == "completed" && syncResult.Result != nil {
nutrition := syncResult.Result
fmt.Printf("Dish: %s\n", nutrition.DishName)
fmt.Printf("Nutri-Score: %s\n", nutrition.NutriScore)
for _, ingredient := range nutrition.Ingredients {
fmt.Printf("- %s: %.1f%s\n", ingredient.Name, ingredient.ServingSize, ingredient.Unit)
}
}
}
```
For complete API specification, data types, and additional parameters, see the [`POST /nutrition_records`](/api-reference/nutrition-ai-analyze-nutrition-image) API Reference.
# Nutrition AI Overview
Source: https://docs.spikeapi.com/nutrition-ai/overview
Overview of the Spike Nutrition AI service and how to use it
## About
Upload food images and receive detailed nutritional analysis powered by AI. The Nutrition API uses advanced computer vision and machine learning to identify ingredients, calculate nutritional values, and provide comprehensive food composition data.
## Getting Started
1. Get an access token using the authentication flow (see [Authentication](/api-docs/authentication))
2. Upload base64-encoded food images using the POST endpoint
3. Retrieve results either synchronously or via webhook notifications
## Key Features
### AI-Powered Analysis
* Advanced computer vision for food identification
* LANGUAL standard terminology for precise ingredient classification
* Machine learning models for accurate nutritional calculations
### Flexible Processing
* **Asynchronous processing** (recommended) — returns immediately with background processing
* **Synchronous processing** — waits for complete analysis before returning results
* **Webhook notifications** — for real-time status updates
### Comprehensive Data
* **29 nutritional fields** from basic macros to detailed micronutrients
* **Ingredient breakdown** with per-item nutritional analysis
* **Nutri-Score rating** (A-E European scale)
* **Localized results** with country and language support
## Technical Requirements
### Image Specifications
* **Minimum size** — 512×512 pixels
* **Maximum size** — 10MB when base64-encoded
* **Supported formats** — JPEG, PNG, WebP
* **Input format** — base64-encoded string
### Processing Workflow
1. **Image Upload & Validation** — system validates format compatibility
2. **AI Model Processing** — multiple AI models are used for analysis
3. **Ingredient Identification** — foods identified using LANGUAL terminology
4. **Nutritional Analysis** — detailed breakdown calculated per 100 g/ml
5. **Translation** (optional) — results translated to specified language
6. **Storage** — results stored with a configurable retention policy
## Analysis Report Data
### Main Data
* Dish name with optional translation
* Dish description with optional translation
* Nutri-Score level (A-E)
* Serving size with unit (g/ml)
### Basic Nutritional Fields
* Energy (kcal)
* Protein (g)
* Total Fat (g)
* Carbohydrates (g)
### Extended Nutritional Data
* **Fats** — saturated, polyunsaturated, monounsaturated, trans fats
* **Micronutrients** — fiber, sugars, cholesterol
* **Minerals** — sodium, potassium, calcium, iron, magnesium, phosphorus, zinc
* **Vitamins** — A, C, D, E, K, B-complex vitamins
See the [Reference](/technical-references/nutritional_fields) for a complete list of nutritional fields.
## Image Guidelines
For optimal analysis results, guide your users to:
1. **Center the food** — capture the contents of the plate as the main subject
2. **Fill the frame** — ensure the meal occupies the majority of the image
3. **Remove distractions** — take off packaging or utensils before photographing
4. **Clean background** — keep background clutter-free, include only the plate
5. **Use proper lighting** — natural or bright lighting to capture food details clearly
6. **Optimal angle** — slight angle to minimize items obscuring each other
7. **Avoid filters** — don't use filters that might alter the food's appearance
## Implementation
For detailed implementation examples, request/response schemas, and code samples, see:
* **[Implementation Guide](/nutrition-ai/implementation)** — complete code examples and integration patterns
* **[Asynchronous Processing](/nutrition-ai/async)** — real-time notifications and webhook implementation
* **[API Reference](/api-reference/nutrition-ai-analyze-nutrition-image)** — detailed endpoint specifications and data types
### Language and Country Options
The API supports translation of results into multiple languages and region-specific analysis through country codes.
By default, responses are in English (en) with the United States (US) as the default country.
Providing a country code parameter improves analysis precision by considering regional ingredients, portion sizes, and food compositions specific to that location.
Supported languages follow ISO 639-1 codes, while country codes adhere to ISO 3166-1 alpha-2 format.
## Response Times
Response times typically range from 4 to 30 seconds depending on image complexity and the completeness of nutrition fields requested. Processing times may vary due to internal factors in AI analysis and nutritional information compilation.
You may use the option for the preference of analysis speed over precision.
# Introduction
Source: https://docs.spikeapi.com/overview
Get started with Spike API - your gateway to health and fitness data integration
# Getting Started with Spike API
Spike API provides a powerful platform for integrating health and fitness data from various providers like Garmin, Fitbit, and more. This guide will help you understand the key differences between using our API directly versus using our SDK.
## Spike API Overview
The Spike API enables seamless integration with health and fitness data providers through:
* Secure authentication using HMAC signatures
* Comprehensive endpoints for data access
* Provider integration management
* User authentication and data management
## API vs SDK: Choosing Your Integration Path
The API offers robust control and server-side processing, while the SDK simplifies integration with automatic handling of common tasks.
### Using the API Directly
```mermaid theme={null}
sequenceDiagram
autonumber
%% Group client actors
box rgb(220,240,255) Client Side
participant ClientApp as Client
Application
participant ClientServer as Client
Server
end
%% Group platform actors
box rgb(255,231,247) Spike Platform
participant SpikeSDK as Spike
SDK
participant SpikeAPI as Spike
API
end
%% Group provider actors
box rgb(255,255,220) External Data
participant Provider as Data
Provider
end
%% Main flow starts
Note right of ClientApp: User requests integration
with a data provider
ClientApp->>+SpikeSDK: Request provider integration
SpikeSDK->>+SpikeAPI: Initiate integration process
deactivate SpikeSDK
SpikeAPI->>Provider: Integrate with provider
Note right of Provider: Integration established
Provider syncs data
Provider-->>SpikeAPI: Continuously sync data
rect rgb(240,255,240)
SpikeAPI->>ClientServer: Send webhook event (new data)
ClientServer->>+SpikeAPI: Query new data details
SpikeAPI-->>-ClientServer: Return integrated data
end
Note over SpikeAPI,Provider: Ongoing data synchronization
deactivate SpikeAPI
```
Ideal for scenarios where:
- Data needs to be available for central processing on a server
- Developers require fine-grained control over data handling
- Custom server-side authentication flows are necessary
For detailed API documentation, visit our [API
Documentation](/api-docs/overview)
### Using the SDK
SDK Integration
```mermaid theme={null}
sequenceDiagram
participant ClientApp as Client Application
participant SpikeSDK as Spike SDK
participant SpikeAPI as Spike API
participant Provider as Data Provider
ClientApp->>SpikeSDK: Request to Integrate with Provider
activate SpikeSDK
SpikeSDK->>SpikeAPI: Start Provider Integration Process
deactivate SpikeSDK
SpikeAPI->>Provider: Establish Integration
Provider->>SpikeAPI: Continuously Synchronize Data
ClientApp->>SpikeSDK: Request Data Retrieval
activate SpikeSDK
SpikeSDK->>SpikeAPI: Forward Data Query
deactivate SpikeSDK
SpikeAPI->>ClientApp: Deliver Retrieved Data
```
Best suited for applications where:
- Data is primarily used within the context of an app
- Simplified integration with minimal setup is preferred
-
Automatic handling of authentication and error management is beneficial
For detailed SDK documentation, visit our [SDK
Documentation](/sdk-docs/overview)
# Android SDK Backfill
Source: https://docs.spikeapi.com/sdk-docs/android/backfill
Backfill historical data from Google Health Connect.
## Backfill from Google Health Connect
Data is stored exclusively on the device (running Android). Backfilling is possible from the moment the application user grants permissions and is limited by:
* What data the user has on their device
* What permissions your app has been granted (read access for specific data types)
* User's health app settings or deletions
* Your retention policy settings
* 30-day period predating permission grant
## Permission Timing and Historical Data Access
Permission request timing is not relevant, meaning that if the application user decides to approve permission a week later by going directly into a Health Connect settings menu, you will be able to access data 23 days before installing the app.
For apps targeting Android API level 34 (Android 14) and higher, Google has introduced [PERMISSION\_READ\_HEALTH\_DATA\_HISTORY](https://developer.android.com/health-and-fitness/guides/health-connect/develop/read-data#read-older-data), which allows access to health data recorded before the app was installed, extending beyond the standard 30-day limitation.
## Manual Data Extraction Required
Because Google doesn't offer any other communication except a Health Connect client, you **must query needed data manually** to backfill it. We recommend keeping this process asynchronous for the best user experience and segmented to ensure the best performance. Before data is extracted, it won't be available to your backend over API calls. Enabling background data delivery also won't perform the backfill as it's designed for new data events.
Ensure your app handles data synchronization efficiently to minimize battery and resource usage. Implement background processing where appropriate to avoid impacting app performance.
## Implementation
```kotlin theme={null}
import java.time.LocalDate
import java.time.ZoneId
import java.time.Instant
// Example: Backfill last 7 days of steps data
val today = LocalDate.now()
for (dayOffset in 0 until 7) {
val startDate = today.minusDays(dayOffset.toLong())
val from: Instant = startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()
val to: Instant = startDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()
try {
// Call the getStatistics method for each day
val statistics = spikeConnection.getStatistics(
types = setOf(StatisticsType.STEPS),
from = from,
to = to,
interval = StatisticsInterval.DAY,
filter = StatisticsFilter(providers = listOf(Provider.HEALTH_CONNECT))
)
// Process the statistics data
statistics.forEach { statistic ->
println("Steps for ${statistic.start}: ${statistic.value}")
}
} catch (e: SecurityException) {
// Handle permission-related errors
println("Permission denied. Guide user to Health Connect settings.")
} catch (e: IllegalArgumentException) {
// Handle invalid parameters
println("Invalid request parameters: ${e.message}")
} catch (e: Exception) {
// Handle other errors gracefully
println("Error fetching statistics for $startDate: ${e.localizedMessage}")
}
}
```
## Best Practices
### Performance and User Experience
* Keep the backfill process asynchronous for the best user experience
* Segment requests into smaller date ranges to ensure optimal performance
* Implement efficient data synchronization to minimize battery usage
* Consider implementing a progress indicator for longer backfill operations
* Use background processing where appropriate to avoid blocking the UI
### Privacy and Security
* Handle permissions gracefully — users may grant partial access
* Consider allowing users to control the backfill scope (date range, data types)
* Comply with relevant regulations such as GDPR for user data protection
### Permission Management
* Ensure your app handles the 30-day limitation appropriately
* Consider implementing `PERMISSION_READ_HEALTH_DATA_HISTORY` for Android 14+ devices
* Guide users to Health Connect settings when permissions are denied
* Regularly check permission status and handle revoked permissions gracefully
### Error Handling and Data Management
* Implement robust error handling for various exception types
* Gracefully handle scenarios where data is unavailable or incomplete
* Manage data conflicts and duplicates effectively to maintain data integrity
* Store health data securely using Android's encryption and security best practices
* Validate data before processing to ensure accuracy and consistency
# Android SDK Background Delivery
Source: https://docs.spikeapi.com/sdk-docs/android/background-delivery
Background delivery ensures that data updates are sent to your backend via webhooks, even when the application is in the background or closed.
### Important Notes About Background Delivery on Android
* Background delivery is scheduled to run every hour, but ultimately, Android decides when the delivery will be executed.
* Android may throttle the frequency of updates for background delivery depending on the app's activity, battery state, etc.
* Android may stop background delivery if it detects that the app is not active for a long time.
* There is a limit of queries that can be done in Health Connect, and it is different for foreground and background reads, so please request only essential data to be delivered in the background. More information in Health Connect documentation.
**Important**: The Spike SDK, along with any other applications, cannot guarantee data synchronization on a fixed schedule. The hourly sync interval serves as a guideline rather than a strict requirement enforced by Android. Consequently, the actual synchronization frequency may vary, occurring hourly, once per day, or during specific system-defined events, such as the conclusion of Sleep Mode or when the device begins charging.
## Setup
Add the following permission to your \`AndroidManifest.xml:
```kotlin theme={null}
```
Check if background delivery is possible with the current Health Connect version:
```kotlin theme={null}
val hcAvailability = spikeConnection.getHealthConnectPermissionManager()
.isFeatureAvailable(HealthConnectFeature.READ_HEALTH_DATA_IN_BACKGROUND)
```
If not, you can ask the user to update Health Connect to the latest version.
Ask for background read permission:
```kotlin theme={null}
val permissionManager = spikeConnection.getHealthConnectPermissionManager()
val requiredPermissions = permissionManager.getPermissions(
// You can add other permissions as well: statistics, sleep, etc.
includeBackgroundDelivery = true
)
val permissionLauncher = rememberLauncherForActivityResult(
permissionManager.getRequestPermissionResultContract(),
onResult = {
...
}
)
permissionLauncher.launch(requiredPermissions)
```
You can also ask for background delivery permission at the same time as other permissions.
Now you can enable background delivery:
```kotlin theme={null}
connection.enableBackgroundDelivery(
BackgroundDeliveryConfig(
statisticsTypes = setOf(StatisticsType.STEPS)
)
)
```
Keep in mind that calling `enableBackgroundDelivery` will overwrite the previous configuration. If you want to add more types, you have to call `enableBackgroundDelivery` again with updated configuration:
```kotlin theme={null}
connection.enableBackgroundDelivery(
BackgroundDeliveryConfig(
statisticsTypes = setOf(StatisticsType.STEPS),
activityTypes = setOf(ActivityType.RUNNING, ActivityType.WALKING)
)
)
```
To check the current configuration, call the `getBackgroundDeliveryConfig()` method.
To stop background delivery, call the `disableBackgroundDelivery()` method.
## Samsung Health
If Samsung Health Data is enabled before enabling background delivery, `enableBackgroundDelivery` will automatically enable Samsung Health Data background delivery.
# Android SDK Changelog
Source: https://docs.spikeapi.com/sdk-docs/android/changelog
A changelog documenting version updates and changes of Spike SDK for Android platform.
## 4.7.12
* Removed the deprecated v1/v2 connection API
## 4.6.12
* Added Health Connect and Samsung Health Data nutrition support. You can now request nutrition permissions, backfill historical nutrition data, and include nutrition fields in background delivery by passing `nutritionalFields`.
```
val nutritionalFields = setOf(
NutritionalField.ENERGY_KCAL,
NutritionalField.PROTEIN_G,
NutritionalField.CARBOHYDRATE_G,
NutritionalField.FAT_TOTAL_G
)
val permissions = spikeConnection.getHealthConnectPermissionManager().getPermissions(
nutritionalFields = nutritionalFields
)
spikeConnection.backfill(
nutritionalFields = nutritionalFields,
days = 7
)
```
* `getNutritionRecords(from, to)` now reads local Health Connect and Samsung Health Data nutrition data before returning nutrition records when those integrations are enabled.
* Nutrition uploads now include magnesium values when provided by Health Connect.
## 4.5.32
* Added Health Connect and Samsung Health Data nutrition support. Pass a `Set` to the Health Connect or Samsung Health Data permission manager to include the nutrition read permission in the request, and the same set is also accepted by `enableBackgroundDelivery`, `backfill`, and `BackgroundDeliveryConfig` so nutrition is uploaded alongside your other data. Once the nutrition permission is granted, `getNutritionRecords(from, to)` automatically pushes the latest local nutrition data before returning analyzed results.
```
val permissions = spikeConnection.getHealthConnectPermissionManager().getPermissions(
statisticsTypes = setOf(StatisticsType.STEPS),
nutritionalFields = setOf(
NutritionalField.ENERGY_KCAL,
NutritionalField.PROTEIN_G,
NutritionalField.CARBOHYDRATE_G,
NutritionalField.FAT_TOTAL_G
)
)
// ... after the user grants `permissions` via the Health Connect request contract ...
val records = spikeConnection.getNutritionRecords(from = startDate, to = endDate)
```
For Samsung Health Data, pass the same `nutritionalFields` set to `getSamsungHealthDataPermissionManager().getPermissions(...)`, `requestPermissions(...)`, or `requestPermissionsFromSamsungHealthDataAndBackfill(...)`.
* Updated Samsung Health Data to 1.1.0
## 4.5.22
* Added `backfill` on the v3 connection to upload historical health data for a chosen number of days. Call it after you have enabled Health Connect and/or Samsung Health Data integration (`enableHealthConnectIntegration()`, `enableSamsungHealthDataIntegration(...)`) and granted the permissions you need via the respective permission managers; then pass the statistics, metrics, activities, and sleep configurations you want to fill in. Only the categories you provide are pushed.
```
spikeConnection.backfill(
statisticsTypes = setOf(StatisticsType.STEPS),
metricTypes = null,
activityConfigs = null,
sleepConfigs = null
)
```
* Added `requestPermissionsFromSamsungHealthDataAndBackfill` — a convenience method that enables Samsung Health Data integration, requests permissions, and immediately starts backfilling historical data. Today and yesterday are pushed before the method returns so you can display data right away; older days continue uploading in the background.
```
spikeConnection.requestPermissionsFromSamsungHealthDataAndBackfill(
activity = this,
statisticsTypes = setOf(StatisticsType.STEPS),
metricTypes = null,
activityConfigs = null,
sleepConfigs = null,
backfillDays = 7
)
```
* Permission `PERMISSION_READ_HEALTH_DATA_HISTORY` is now added automatically to the list if required according to configuration in admin console.
## 4.5.12
* Improved handling of deleted integrations — background delivery is now automatically disabled when the integration no longer exists
* Improved error handling for API responses
* Added `RecordConfig` for configuring records queries:
* `includeSamples` - whether to include samples (raw data points) in the response
* Updated `getRecords` to accept an optional `RecordConfig` parameter:
```
spikeConnection.getRecords(
types = metricTypes,
from = startDate,
to = endDate,
filter = StatisticsFilter(excludeManual = true),
config = RecordConfig(includeSamples = true)
)
```
## 4.4.12
* Updated `Unit` enum with new values:
* Added: `miles`, `mPerSec`, `fahrenheit`, `ms`, `seconds`, `degrees`, `sleepStage`, `secPerM`, `rpm`, `spm`, `breathsPerMin`, `g`, `lbs`, `st`, `mmHg`, `mLPerKgPerMin`, `uV`, `mgPerDl`, `w`
* Removed: `kmh`, `kmPerMin`
* Improved data communication with API
* Enhanced the `getSleep` endpoint.
Important:
* Dates provided are now interpreted as dates only; the time component is ignored when querying the API.
* The primary night sleep period is associated with the calendar date on which it ends.
## 4.3.142
* Internal improvements and bug fixes.
## 4.3.132
* Samsung Health Data Integration - Major Expansion:
* Added support for reading and pushing the following data types from Samsung Health:
* Workouts (exercises)
* Sleep data
* Body composition data (weight, height, BMI, body fat)
* User profile data (weight, height, birth date, gender)
* Blood pressure measurements
* Skin temperature measurements
* Heart rate measurements
* Blood oxygen saturation (SpO2) measurements
* Body temperature measurements
* More efficient Samsung Health Data pushes
* Large Samsung Health Data pushes are now automatically split into smaller chunks for reliable delivery
* Health Connect Improvements:
* Large Health Connect pushes are now automatically split into smaller chunks for reliable delivery
## 4.3.122
* Added Nutrition Facts Label Recognition for extracting nutritional information from label photos.
For more information please visit our [documentation](https://docs.spikeapi.com).
* `recognizeNutritionFactsLabel(imageBase64, config)` - Analyze a nutrition facts label image and extract nutritional data
* `recognizeNutritionFactsLabel(image: Bitmap, config)` - Convenience method that accepts Bitmap
* Added methods for manual nutrition record management:
* `createNutritionRecord(nutritionRecord)` - Insert a new nutrition record with custom data
* `replaceNutritionRecord(nutritionRecord)` - Replace an existing nutrition record completely
* Added `NutritionRecordUtils` for creating and manipulating nutrition records:
* `NutritionRecordUtils.create(...)` - Create a new nutrition record with specified properties
* `NutritionRecord.withIngredient(ingredient)` - Add an ingredient to a record
* `NutritionRecord.withoutIngredient(ingredient)` - Remove an ingredient from a record
* `NutritionRecord.withServingSize(servingSize, unit)` - Update serving size and unit
* Added `NutritionRecordIngredientUtils` for creating and manipulating ingredients:
* `NutritionRecordIngredientUtils.create(...)` - Create a new nutrition record ingredient
* `NutritionRecordIngredient.withNutritionalField(field, value)` - Set a nutritional field value
* `NutritionRecordIngredient.withoutNutritionalField(field)` - Remove a nutritional field
* `NutritionRecordIngredient.withScaledServingSize(newServingSize)` - Scale serving size and nutritional values proportionally
* Nutrition AI methods now return `NutritionRecordAnalysisResult` instead of `NutritionRecord`.
The new `NutritionRecordAnalysisResult` contains:
* `recordId` - The record ID
* `status` - Processing status (pending, processing, completed, failed, updated)
* `failureReason` - Reason for failure (if status is failed)
* `uploadedAt` - Upload timestamp
* `nutritionRecord` - The actual nutrition data (nullable, only present when status is completed/updated)
**Affected methods:**
* `analyzeNutrition(...)` - Now returns `NutritionRecordAnalysisResult`
* `getNutritionRecords(from, to)` - Now returns `List`
* `getNutritionRecord(id)` - Now returns `NutritionRecordAnalysisResult?`
* `updateNutritionRecordServingSize(id, servingSize)` - Now returns `NutritionRecordAnalysisResult`
* `createNutritionRecord(nutritionRecord)` - Now returns `NutritionRecordAnalysisResult`
* `replaceNutritionRecord(nutritionRecord)` - Now returns `NutritionRecordAnalysisResult`
Access the nutrition data via the `nutritionRecord` property:
```kotlin theme={null}
val result = spikeConnection.analyzeNutrition(image)
val dishName = result.nutritionRecord?.dishName // Previously: result.dishName
val status = result.status // Status is now on the result object
```
* `NutritionRecord` no longer contains `status` and `failureReason` properties.
These properties have been moved to `NutritionRecordAnalysisResult`.
## 4.3.112
* Added Nutrition AI features for analyzing food images. For more information
please visit our [documentation](https://docs.spikeapi.com).
* `analyzeNutrition(imageBase64, consumedAt, config)` - Analyze food image synchronously and wait for results
* `analyzeNutrition(image: Bitmap, consumedAt, config)` - Convenience method that accepts Bitmap
* `submitNutritionForAnalysis(imageBase64, consumedAt, config)` - Analyze food image asynchronously, returns record ID
* `submitNutritionForAnalysis(image: Bitmap, consumedAt, config)` - Convenience method that accepts Bitmap
* `getNutritionRecords(from, to)` - Retrieve nutrition records for a date range
* `getNutritionRecord(id)` - Get a specific nutrition record by ID
* `deleteNutritionRecord(id)` - Delete a nutrition record
* `updateNutritionRecordServingSize(id, servingSize)` - Update serving size for a nutrition record
* Added new `Provider`:
* LUNA
* Added new `ProviderSource` types:
* FITBIT\_SKIN\_TEMPERATURE\_SUMMARY
* GARMIN\_WELLNESS\_SKIN\_TEMPERATURE\_SUMMARY
* LUNA\_SLEEP
* Removed `ProviderSource` types:
* FITBIT\_USER\_ACTIVITIES\_LIST
* FITBIT\_USER\_ACTIVITIES\_DATE
* FITBIT\_USER\_ACTIVITIES\_HEART\_DATE
* FITBIT\_USER\_ACTIVITIES\_HEART\_DATE\_INTERDAY
* WITHINGS\_MEASURE\_GET\_WORKOUTS
* WITHINGS\_MEASURE\_GET\_ACTIVITY
* WITHINGS\_MEASURE\_GET\_MEAS
* Removed `ActivityTag`:
* ON\_BICYCLE
* Removed `Provider`:
* WAHOO
## 4.3.102
* Enable Health Connect integration in admin console right after enabling integration
* Enable Samsung Health Data integration in admin console right after enabling integration
* 🔴 **Breaking Change**: `enableHealthConnectIntegration()` is now a suspend function. Update your code to call it from a coroutine scope.
* 🔴 **Breaking Change**: `enableSamsungHealthDataIntegration()` is now a suspend function. Update your code to call it from a coroutine scope.
## 4.3.92
* Updated Health connect to `1.1.0-rc03`
* Updated compileSdk to `36`
* Updated gradle to `8.13`
* Removed `providerUserIdentifier` from `IntegrationInitConfig`
* Added ability to read mindfulness activities:
```
if (spikeConnection.getHealthConnectPermissionManager()
.isFeatureAvailable(feature: HealthConnectFeature.FEATURE_MINDFULNESS_SESSION)) {
spikeConnection.getActivities(
config: ActivityConfig(activityCategories: [ActivityCategory.MINDFULNESS])
from: dateFrom,
to: dateTo
)
}
```
* Added new `ActivityTag` types:
* MINDFULNESS
* Added new `ActivityType` types:
* ECG\_MEASUREMENT
* TRIATHLON
* BIATHLON
* DUATHLON
* ROLLERBLADING
* SKATEBOARDING
* SKATING
* CALISTHENICS
* WEIGHT\_LIFTING
* CANOEING
* FLOORBALL
* JIU\_JITSU
* DIVING
* ORIENTEERING
* BOOTCAMP
* MOTORSPORTS
* HORSERIDING
* PARAGLIDING
* MULTISPORT
* BLOOD\_TEST
* MINDFULNESS\_SESSION
* Added new `ProviderSource` types:
* APPLE\_HEALTHKIT\_MINDFULNESS
* HEALTH\_CONNECT\_MINDFULNESS\_SESSION
* Added new `ActivityCategory` type:
* MINDFULNESS
* Added new `HealthConnectFeature`:
* FEATURE\_MINDFULNESS\_SESSION
* Added new `MetricType`:
* HEARTRATE\_RESTING\_MIN
* HEARTRATE\_RESTING\_MAX
* CADENCE
* CADENCE\_MIN
* CADENCE\_MAX
* PACE
* AIR\_TEMPERATURE
* BODY\_TEMPERATURE\_MAX
* BODY\_TEMPERATURE\_MIN
* BASAL\_BODY\_TEMPERATURE
* BASAL\_BODY\_TEMPERATURE\_MAX
* BASAL\_BODY\_TEMPERATURE\_MIN
* SKIN\_TEMPERATURE\_MAX
* SKIN\_TEMPERATURE\_MIN
* SLEEP\_SKIN\_TEMPERATURE\_DEVIATION
* Added new `StatisticsType`:
* SLEEP\_SKIN\_TEMPERATURE\_DEVIATION
* HRV\_RMSSD
* HRV\_SDNN
## 4.3.82
* Workaround for the Android 15 bug to make background delivery more resilient
* Implemented more efficient pushes to Spike API
## 4.3.72
* Allow reading `SPO2` and `SWIMMING_LENGTHS` in `getActivities`
* Allow reading `SPO2` and `SKIN_TEMPERATURE` in `getSleeps`
## 4.3.62
* Optimized background delivery
## 4.3.52
* Added `Provider.COROS`
* Updated `ProviderSource` enum
* Added new Statistic types:
* STRESS\_SCORE
* RECOVERY\_SCORE
* ACTIVITY\_SCORE
* Added new Metric types for `getSleep` request:
* SLEEP\_DURATION
* SLEEP\_DURATION\_AWAKE
* SLEEP\_DURATION\_DEEP
* SLEEP\_DURATION\_LIGHT
* SLEEP\_DURATION\_NAP
* SLEEP\_DURATION\_REM
* SLEEP\_EFFICIENCY
* SLEEP\_INTERRUPTIONS
* SLEEP\_LATENCY
* SLEEP\_SCORE
## 4.3.42
* Added new `MetricType.GLUCOSE` available in both Health Connect and Samsung Health
* Updated Samsung Health Data SDK to version 1.0.0 (available only on Android 29+)
## 4.3.32
* Switched to protobuf java-lite 3.25.5 for better compatibility with firebase
* The old SpikeSDK API has been deprecated
## 4.3.22
* Added DISTANCE\_SWIMMING into the list of metrics available in activities
* Added new providers: DEXCOM, FREESTYLE\_LIBRE, HUAWEI, STRAVA
## 4.3.12
* Added new statistics for Health Connect:
* HEARTRATE
* HEARTRATE\_MAX
* HEARTRATE\_MIN
* Added new metrics for Health Connect:
* HEARTRATE
* HEARTRATE\_MAX
* HEARTRATE\_MIN
* Improved transport protocol for even faster requests to Spike API
* Added Samsung Health Integration for:
* `StatisticType`s:
* STEPS
* DISTANCE\_TOTAL
* CALORIES\_BURNED\_ACTIVE
* CALORIES\_BURNED\_TOTAL
* CALORIES\_BURNED\_BASAL
* Added new `Provider`:
* SAMSUNG\_HEALTH\_DATA
* Added new `ProviderSource`:
* SAMSUNG\_HEALTH\_DATA\_AGGREGATION
* Renamed `MetricType`s:
* SLEEP\_BREATHING\_RATE to BREATHING\_RATE
* SLEEP\_BREATHING\_RATE\_MIN to BREATHING\_RATE\_MIN
* SLEEP\_BREATHING\_RATE\_MAX to BREATHING\_RATE\_MAX
## 4.2.82
* Added `IntegrationInitConfigUtils` for usage in Flutter and React Native libraries
* Additional check in `getIntegrationInitUrl` for email in ultrahuman integration
## 4.2.72
* Added new `MetricType`s:
* BODY\_FAT
* BODY\_FAT\_MAX
* BODY\_FAT\_MIN
* BODY\_BONE\_MASS
* BODY\_MASS\_INDEX
* BLOOD\_PRESSURE\_SYSTOLIC
* BLOOD\_PRESSURE\_SYSTOLIC\_MIN
* BLOOD\_PRESSURE\_SYSTOLIC\_MAX
* BLOOD\_PRESSURE\_DIASTOLIC
* BLOOD\_PRESSURE\_DIASTOLIC\_MIN
* BLOOD\_PRESSURE\_DIASTOLIC\_MAX
* Added new fields in `UserProperties`:
* BODY\_FAT
* BODY\_BONE\_MASS
* BODY\_MASS\_INDEX
* Updated `getIntegrationInitUrl`. Now it accepts `IntegrationInitConfig` config object where you can pass:
* `redirectUri`: will override the one set in admin console
* `state`: when the authorization server redirects back to the client, it includes the `state` value originally sent
* `providerUserIdentifier`: at the moment used (and required) only when integrating with ultrahuman
* Added new provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide Ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider = Provider.ULTRAHUMAN, config = IntegrationInitConfig(providerUserIdentifier = "user@mail.com"))
```
## 4.2.62
* Added consumer proguard rules
## 4.2.52
* New statistics:
* HEARTRATE\_RESTING
* SLEEP\_DURATION\_TOTAL
* New statistics (only from non-HealthConnect providers):
* HEARTRATE
* HEARTRATE\_MIN
* HEARTRATE\_MAX
* Metric types updates
* Better proguard settings for uniqueness of generated class names
* Fix for statistics in different time zones
## 4.2.42
* New metrics:
* SPO2
* BODY\_TEMPERATURE
* SKIN\_TEMPERATURE
* SLEEP\_BREATHING\_RATE (available only on getSleep())
* SLEEP\_BREATHING\_RATE\_MIN (available only on getSleep())
* SLEEP\_BREATHING\_RATE\_MAX (available only on getSleep())
* SLEEP\_BREATHING\_RATE\_AVG (available only on getSleep())
* Added sleepScore property in the Record type
## 4.2.32
* New `getUserProperties` method for reading: weight, height, timezone from Health Connect
* New `getUserProperties` method for reading: birthdate, gender from other providers
* New metric: `VO2MAX` available in `getRecords` and `getActivities`
* New statistic: `SLEEP_SCORE`
## 4.2.22
* Implemented better API error handling
* Exposed `SpikeHMACSignatureGenerator` for debugging purposes
## 4.2.12
* 🔴 **Breaking Change**: Update to `SpikeConnectionAPIv3.createConnection`. The method
`SpikeConnectionAPIv3.createConnection(context:, appId:, authToken:, customerEndUserId:)` has been updated to:
`SpikeConnectionAPIv3.createConnection(context:, applicationId:, signature:, endUserId:)`
* The `applicationId` parameter must now be provided as an **`Int`**.
* The `signature` parameter now requires an **`HMAC-SHA256` signed user ID**.
* ⚠️ **Security Notice:**
* **Do not store your HMAC signing key within the application itself**, as this poses a security risk.
* Instead, generate and provide the signature from your backend.
* **Legacy Support**: For **development purposes only**, the previous connection flow remains available under the renamed method:
`SpikeConnectionAPIv3.createConnectionLegacy(context:, appId:, authToken:, customerEndUserId:)`
## 4.1.12
* Added background delivery (see documentation for more information)
* Health Connect library updated to 1.1.0-alpha11
* Compile SDK and target SDK updated to 35
* Updated kotlin version to 1.9.25
* Updated other dependencies
* Added log callback
* Updated gradle to 8.8.0
## 4.0.22
Renamed `getProviderIntegrationUrl` to `getIntegrationInitUrl` to be on par with other platforms
## 4.0.12
Completely new SDK! Please see our official documentation for more details and usage instructions
## 3.1.6
* Updated steps intraday to better check for manual entries
* Added permissions specific to steps\_intraday
## 3.1.5
* Implemented checking for permissions before reading additional sleep data
## 3.1.4
* Added metadata in Calories raw data
## 3.1.3
* Reverted 3.1.2 changes
## 3.1.2
* Added third party integration initialization
## 3.1.1
* Removed unneeded settings from android manifest
## 3.1.0
* Better way to authenticate with API
* Added saved session validation before the connection is established
* Added customer user id validation.
## 3.0.19
* Added safeguard for to handle multiple sources of sleep data and avoid illogical values
## 3.0.18
* Added new logic for detecting changes in intraday\_steps metadata
## 3.0.17
* Removed the limitation that prevented values from being sent when the requested period is more than a day
* Implemented calculation of time by sleep stage in sleep data
## 3.0.16
* Updated metadata info in intraday\_steps to have all values as single objects
* Refactored how the decision whether to send metadata with intraday\_steps is made
## 3.0.15
* Added all metadata info to each intraday\_steps data object
## 3.0.14
* Fixed sleep time counting by different stages
## 3.0.13
* Added a source field to the sleep data class
## 3.0.11
* Fixed data type StepsIntraday handling
## 3.0.9
* Added a new Spike data type — `IntradaySteps`
## 3.0.8
* Updated the package checker function to avoid unnecessary checks for Android 14 and up versions.
## 3.0.7
* Can get permission contract before SpikeConnection is created
## 3.0.6
* Moved permission checking before data extraction to the SDK connection layer
## 3.0.5
* Fixed extraction of heart data
## 3.0.4
* Added support for Android 14
* Updated Readme to match the Android 14 implementation process
* Fixed some issues with data requesting for a specified date range
## 3.0.2:
* Activity summary and activity stream can be called without granting all their permissions. Returned data depends on which permissions were provided.
* Permission requests are divided by datatypes.
* Works up to Android 13.
# Android SDK Logging
Source: https://docs.spikeapi.com/sdk-docs/android/logging
Getting logs for troubleshooting and debugging from Spike SDK for Android platform.
## Code Examples
By default, SPike SDK logs are sent to standard Android Logcat. If for some reason you want to read them, you can use the following code (running it before you start using SpikeSDK):
```kotlin theme={null}
SpikeConnectionAPIv3.setLogCallback { level, message ->
when (level) {
LogLevel.VERBOSE -> {}
LogLevel.DEBUG -> if(BuildConfig.DEBUG) {
Log.d("SpikeSDK", message)
}
LogLevel.WARNING -> Log.w("SpikeSDK", message)
LogLevel.ERROR -> Log.e("SpikeSDK", message)
}
}
```
If you use Background Delivery, this code should be run in your application's onCreate() method:
```kotlin theme={null}
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
SpikeConnectionAPIv3.setLogCallback { level, message ->
when (level) {
LogLevel.VERBOSE -> {}
LogLevel.DEBUG -> if(BuildConfig.DEBUG) {
Log.d("SpikeSDK", message)
}
LogLevel.WARNING -> Log.w("SpikeSDK", message)
LogLevel.ERROR -> Log.e("SpikeSDK", message)
}
}
}
}
```
Also, the application should be set in AndroidManifest.xml:
```kotlin theme={null}
```
# Android SDK Nutrition AI
Source: https://docs.spikeapi.com/sdk-docs/android/nutrition-ai
Analyze food images and retrieve nutritional information using AI-powered analysis in your Android app.
## About
The Spike SDK provides a convenient interface for the [Nutrition AI API](/nutrition-ai/overview), allowing you to analyze food images directly from your Android application. The SDK handles image encoding, API communication, and response parsing, making it easy to integrate nutritional analysis into your app.
All Spike SDK suspending methods should be called from a coroutine scope and wrapped in try-catch blocks. See [Error Handling](#error-handling) for details.
## Key Features
* **AI-Powered Analysis** — advanced computer vision for food identification and nutritional calculations
* **Flexible Processing** — choose between synchronous (wait for results) or asynchronous (background) processing
* **`Bitmap` Support** — convenient methods that accept `Bitmap` directly, in addition to base64-encoded strings
* **Complete Record Management** — retrieve, update, and delete nutrition records
## Available Methods
| Method | Description |
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `analyzeNutrition(image, consumedAt, config)` | Submit food image for synchronous processing and wait for the analysis results |
| `submitNutritionForAnalysis(image, consumedAt, config)` | Submit food image for asynchronous processing and get record ID immediately for polling afterwards |
| `getNutritionRecords(from, to)` | Retrieve nutrition records for a datetime range |
| `getNutritionRecord(id)` | Get a specific nutrition record by ID |
| `updateNutritionRecordServingSize(id, servingSize)` | Update serving size for a nutrition record |
| `deleteNutritionRecord(id)` | Delete a nutrition record by ID |
## Analyzing Food Images
### Synchronous Processing
Use synchronous analysis when you want to wait for the complete nutritional analysis before proceeding. This is ideal for scenarios where you need immediate results and can display a loading indicator.
```kotlin theme={null}
import com.spikeapi.apiv3.SpikeConnectionAPIv3
import com.spikeapi.apiv3.datamodels.*
import java.time.Instant
// image: Bitmap - captured from camera or photo library
val image: Bitmap = // ... captured from camera or gallery
try {
val record = spikeConnection.analyzeNutrition(
image = image,
consumedAt = Instant.now(),
config = NutritionalAnalysisConfig(
analysisMode = NutritionRecordAnalysisMode.PRECISE,
countryCode = null,
languageCode = null,
includeNutriScore = true,
includeDishDescription = true,
includeIngredients = true,
includeNutritionFields = listOf(
NutritionalField.ENERGY_KCAL,
NutritionalField.PROTEIN_G,
NutritionalField.FAT_TOTAL_G,
NutritionalField.CARBOHYDRATE_G
)
)
)
println("Dish: ${record.dishName ?: "Unknown"}")
println("Serving size: ${record.servingSize ?: 0} ${record.unit?.value ?: "g"}")
println("Calories: ${record.nutritionalFields?.get("energy_kcal") ?: 0}")
} catch (e: Exception) {
println("Analysis failed: ${e.message}")
}
```
You can also use base64-encoded image data:
```kotlin theme={null}
// Using base64-encoded string
val outputStream = ByteArrayOutputStream()
image.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)
val base64String = Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP)
val record = spikeConnection.analyzeNutrition(
imageBase64 = base64String,
consumedAt = Instant.now(),
config = null // Uses default configuration
)
```
**Processing Time**: Synchronous processing takes some time depending on image complexity. Consider showing a loading indicator to users. If you see that the analysis is taking too long, the recommendation is to use asynchronous processing instead.
### Asynchronous Processing
Use asynchronous processing when you want an immediate response without waiting for the analysis to complete. Record ID is returned.
The image is processed in the background, and you can retrieve results later by requesting nutrition analysis using the record ID or receive them via webhook.
```kotlin theme={null}
try {
// Submit image for background processing
val recordId: UUID = spikeConnection.submitNutritionForAnalysis(
image = image,
consumedAt = Instant.now(),
config = NutritionalAnalysisConfig(
analysisMode = NutritionRecordAnalysisMode.FAST,
countryCode = null,
languageCode = null,
includeNutriScore = null,
includeDishDescription = null,
includeIngredients = true,
includeNutritionFields = null
)
)
println("Analysis started. Record ID: $recordId")
// Optionally, poll for results later
// Your backend will also receive a webhook when analysis completes
} catch (e: Exception) {
println("Failed to submit: ${e.message}")
}
```
#### Retrieving Results Asynchronously
After submitting an image for asynchronous processing, you can retrieve the results using the record ID. Check the processing status for completion success.
```kotlin theme={null}
// Check the status and get results
val record = spikeConnection.getNutritionRecord(id = recordId)
if (record != null) {
when (record.status) {
NutritionRecordStatus.COMPLETED -> {
println("Analysis complete: ${record.dishName ?: "Unknown"}")
}
NutritionRecordStatus.PROCESSING -> {
println("Still processing...")
}
NutritionRecordStatus.PENDING -> {
println("Queued for processing...")
}
NutritionRecordStatus.FAILED -> {
println("Analysis failed: ${record.failureReason ?: "Unknown error"}")
}
NutritionRecordStatus.UNKNOWN -> {
println("Unknown status")
}
}
}
```
For real-time notifications, configure webhooks in your [admin console](https://admin.spikeapi.com/). Your backend will receive a webhook notification when the analysis completes. See [Asynchronous Processing](/nutrition-ai/async) for webhook implementation details.
## Configuration Options
Customize the analysis using `NutritionalAnalysisConfig`:
```kotlin theme={null}
val config = NutritionalAnalysisConfig(
// Analysis speed vs. precision
analysisMode = NutritionRecordAnalysisMode.PRECISE, // PRECISE (default) or FAST
// Country ISO 3166-1 alpha-2 code in lowercase
countryCode = "us",
// Language ISO 639-1 code in lowercase
languageCode = "en",
// Include Nutri-Score rating (A-E)
includeNutriScore = true,
// Include dish description
includeDishDescription = true,
// Include detailed breakdown of ingredients
includeIngredients = true,
// Specify which nutritional fields to include (using NutritionalField enum)
includeNutritionFields = listOf(
NutritionalField.ENERGY_KCAL,
NutritionalField.PROTEIN_G,
NutritionalField.FAT_TOTAL_G,
NutritionalField.CARBOHYDRATE_G,
NutritionalField.FIBER_TOTAL_DIETARY_G,
NutritionalField.SODIUM_MG
)
)
```
### `NutritionalAnalysisConfig`
```kotlin theme={null}
data class NutritionalAnalysisConfig(
/** A preferred mode for the analysis. Default is PRECISE. */
val analysisMode: NutritionRecordAnalysisMode?,
/** Country ISO 3166-1 alpha-2 code in lowercase */
val countryCode: String?,
/** Language ISO 639-1 code in lowercase */
val languageCode: String?,
/** Include nutri-score label of the food. Default is false. */
val includeNutriScore: Boolean?,
/** Include dish description of the food. Default is false. */
val includeDishDescription: Boolean?,
/** Include ingredients of the food. Default is false. */
val includeIngredients: Boolean?,
/**
* Include specific nutrition fields in the analysis report.
* By default, carbohydrate_g, energy_kcal, fat_total_g and protein_g will be included.
*/
val includeNutritionFields: List?
)
```
### Analysis Modes
```kotlin theme={null}
enum class NutritionRecordAnalysisMode(val value: String) {
FAST("fast"),
PRECISE("precise")
}
```
| Mode | Description |
| --------- | ---------------------------------------------------------------------------- |
| `PRECISE` | Uses advanced AI models for highest accuracy and detailed analysis (default) |
| `FAST` | Uses optimized models for quicker processing with good accuracy |
### Default Nutritional Fields
If `includeNutritionFields` is not specified, only these basic fields are included:
* `ENERGY_KCAL`
* `PROTEIN_G`
* `FAT_TOTAL_G`
* `CARBOHYDRATE_G`
See [Nutritional Fields Reference](/technical-references/nutritional_fields) for all available fields
or check the [API Reference](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-nutritional-field/index.html) for Kotlin enum values.
## Managing Nutrition Records
### List Records by Date Range
Retrieve all nutrition records within a specified date range:
```kotlin theme={null}
import java.time.Instant
import java.time.temporal.ChronoUnit
val endDate = Instant.now()
val startDate = endDate.minus(7, ChronoUnit.DAYS)
try {
val records = spikeConnection.getNutritionRecords(
from = startDate,
to = endDate
)
for (record in records) {
val consumedAt = record.consumedAt?.toString() ?: "Unknown date"
val size = record.servingSize ?: 0.0
val unit = record.unit?.value ?: "g"
println("$consumedAt: ${record.dishName ?: "Unknown"} - $size$unit")
}
} catch (e: Exception) {
println("Failed to fetch records: ${e.message}")
}
```
### Get a Specific Record
Retrieve a single nutrition record by its ID:
```kotlin theme={null}
try {
val record = spikeConnection.getNutritionRecord(id = recordId)
if (record != null) {
println("Dish: ${record.dishName ?: "Unknown"}")
println("Nutri-Score: ${record.nutriScore ?: "N/A"}")
// Access nutritional values
record.nutritionalFields?.get("energy_kcal")?.let { calories ->
println("Calories: $calories kcal")
}
// Access ingredients if included
record.ingredients?.forEach { ingredient ->
println("- ${ingredient.name}: ${ingredient.servingSize}${ingredient.unit.value}")
}
} else {
println("Record not found")
}
} catch (e: Exception) {
println("Failed to fetch record: ${e.message}")
}
```
### Update Serving Size
Adjust the serving size of an existing record. All nutritional values are automatically recalculated proportionally:
```kotlin theme={null}
try {
val updatedRecord = spikeConnection.updateNutritionRecordServingSize(
id = recordId,
servingSize = 200.0 // New serving size in grams
)
println("Updated serving size: ${updatedRecord.servingSize ?: 0}${updatedRecord.unit?.value ?: "g"}")
println("Recalculated calories: ${updatedRecord.nutritionalFields?.get("energy_kcal") ?: 0}")
} catch (e: Exception) {
println("Failed to update record: ${e.message}")
}
```
### Delete a Record
Permanently remove a nutrition record (success status is returned regardless record is found or not):
```kotlin theme={null}
try {
spikeConnection.deleteNutritionRecord(id = recordId)
println("Record deleted successfully")
} catch (e: Exception) {
println("Failed to delete record: ${e.message}")
}
```
## Response Data
### `NutritionRecord`
The `NutritionRecord` data class contains the analysis results:
```kotlin theme={null}
data class NutritionRecord(
/** Report record ID */
val recordId: UUID,
/** Processing status */
val status: NutritionRecordStatus,
/** Detected dish name */
val dishName: String?,
/** Detected dish description */
val dishDescription: String?,
/** Dish name translated to target language */
val dishNameTranslated: String?,
/** Dish description translated to target language */
val dishDescriptionTranslated: String?,
/** Nutri-Score known as the 5-Colour Nutrition label (A-E) */
val nutriScore: String?,
/** Reason for processing failure */
val failureReason: String?,
/** Serving size in metric units */
val servingSize: Double?,
/** Metric unit (g for solids, ml for liquids) */
val unit: NutritionalUnit?,
val nutritionalFields: Map?,
/** List of detected ingredients with nutritional information */
val ingredients: List?,
/** Upload timestamp in UTC */
val uploadedAt: Instant,
/** Update timestamp in UTC */
val modifiedAt: Instant,
/** The UTC time when food was consumed */
val consumedAt: Instant?
)
```
### `NutritionRecordStatus`
```kotlin theme={null}
enum class NutritionRecordStatus(val value: String) {
PENDING("pending"),
PROCESSING("processing"),
COMPLETED("completed"),
FAILED("failed"),
UNKNOWN("_unknown") // Unknown value was sent from API. SDK should be updated to use the newest API responses.
}
```
### `NutritionalUnit`
```kotlin theme={null}
enum class NutritionalUnit(val value: String) {
G("g"), // grams
MG("mg"), // milligrams
MCG("mcg"), // micrograms
ML("ml"), // milliliters
KCAL("kcal"), // kilocalories
UNKNOWN("_unknown") // Unknown value was sent from API. SDK should be updated to use the newest API responses.
}
```
### `NutritionRecordIngredient`
```kotlin theme={null}
data class NutritionRecordIngredient(
/** Ingredient name using LANGUAL standard terminology */
val name: String,
/** Ingredient name translated to target language */
val nameTranslated: String?,
/** Serving size in metric units */
val servingSize: Double,
/** Metric unit (g for solids, ml for liquids) */
val unit: NutritionalUnit,
val nutritionalFields: Map?
)
```
### `NutritionalField`
Use this enum to specify which nutritional fields to include in the analysis:
```kotlin theme={null}
enum class NutritionalField(val value: String) {
ENERGY_KCAL("energy_kcal"),
CARBOHYDRATE_G("carbohydrate_g"),
PROTEIN_G("protein_g"),
FAT_TOTAL_G("fat_total_g"),
FAT_SATURATED_G("fat_saturated_g"),
FAT_POLYUNSATURATED_G("fat_polyunsaturated_g"),
FAT_MONOUNSATURATED_G("fat_monounsaturated_g"),
FAT_TRANS_G("fat_trans_g"),
FIBER_TOTAL_DIETARY_G("fiber_total_dietary_g"),
SUGARS_TOTAL_G("sugars_total_g"),
CHOLESTEROL_MG("cholesterol_mg"),
SODIUM_MG("sodium_mg"),
POTASSIUM_MG("potassium_mg"),
CALCIUM_MG("calcium_mg"),
IRON_MG("iron_mg"),
MAGNESIUM_MG("magnesium_mg"),
PHOSPHORUS_MG("phosphorus_mg"),
ZINC_MG("zinc_mg"),
VITAMIN_ARAE_MCG("vitamin_a_rae_mcg"),
VITAMIN_CMG("vitamin_c_mg"),
VITAMIN_DMCG("vitamin_d_mcg"),
VITAMIN_EMG("vitamin_e_mg"),
VITAMIN_KMCG("vitamin_k_mcg"),
THIAMIN_MG("thiamin_mg"),
RIBOFLAVIN_MG("riboflavin_mg"),
NIACIN_MG("niacin_mg"),
VITAMIN_B6MG("vitamin_b6_mg"),
FOLATE_MCG("folate_mcg"),
VITAMIN_B12MCG("vitamin_b12_mcg")
}
```
## Error Handling
All nutrition methods are suspending functions that can throw exceptions. Always wrap calls in try-catch blocks:
```kotlin theme={null}
import com.spikeapi.SpikeExceptions
try {
val record = spikeConnection.analyzeNutrition(
image = image,
consumedAt = Instant.now(),
config = null
)
// Handle success
} catch (e: SpikeExceptions.SpikeException) {
println("Spike error: ${e.message}")
} catch (e: SpikeExceptions.NetworkException) {
println("Network error: ${e.message}")
} catch (e: SpikeExceptions.AuthenticationException) {
println("Authentication failed: ${e.message}")
} catch (e: Exception) {
println("Unexpected error: ${e.message}")
}
```
### Common Error Scenarios
| Error | Cause |
| -------------------- | --------------------------------------- |
| Invalid image format | Image is not JPEG, PNG, or WebP |
| Image too large | Base64-encoded image exceeds 10MB |
| Image too small | Image is smaller than 512×512 pixels |
| Unauthorized | Invalid or expired authentication token |
| Analysis timeout | AI processing took too long |
| Unidentifiable | Non-food image |
## Image Guidelines
For optimal analysis results, guide your users to capture images that:
1. **Center the food** — capture the plate contents as the main subject
2. **Fill the frame** — ensure the meal occupies most of the image
3. **Use proper lighting** — natural or bright lighting works best
4. **Avoid obstructions** — remove packaging and minimize utensils in frame
5. **Skip filters** — avoid filters that alter the food's appearance
See [Image Guidelines](/nutrition-ai/overview#image-guidelines) for complete recommendations.
## Best Practices
### 1. Request Only What You Need
Each additional field, ingredient breakdown, or optional data increases processing time. Only request what your app actually uses:
```kotlin theme={null}
// ❌ Don't request everything "just in case"
val config = NutritionalAnalysisConfig(
analysisMode = null,
countryCode = null,
languageCode = null,
includeNutriScore = true,
includeDishDescription = true,
includeIngredients = true,
includeNutritionFields = NutritionalField.entries // All 29 fields
)
// ✅ Request only what you need
val config = NutritionalAnalysisConfig(
includeDishDescription = true,
includeNutritionFields = listOf(
NutritionalField.ENERGY_KCAL
)
)
```
### 2. Consider your actual UI requirements:
* Do you display ingredients? If not, skip `includeIngredients`.
* Do you show Nutri-Score? If not, skip `includeNutriScore`.
* Which nutritional values do you actually display? Request only those.
### 3. Choose the Right Processing Mode
* **Synchronous** (`analyzeNutrition`): Use when you need immediate results and can show a loading state
* **Asynchronous** (`submitNutritionForAnalysis`): Use for better UX when you don't need immediate results, or when processing multiple images
### 4. Handle All Status Values
When using asynchronous processing, always check the record status before accessing results:
```kotlin theme={null}
val record = spikeConnection.getNutritionRecord(id = recordId)
if (record == null) {
// Handle not found
return
}
if (record.status != NutritionRecordStatus.COMPLETED) {
if (record.status == NutritionRecordStatus.FAILED) {
// Handle failure
println("Failed: ${record.failureReason}")
} else {
// Still processing
println("Status: ${record.status}")
}
return
}
// Safe to access results
```
### 5. Implement Webhook Handling
For production apps using asynchronous processing, implement [webhook handling](/nutrition-ai/async) on your backend to receive real-time notifications when analysis completes.
### 6. Cache Configuration
Create a shared configuration object if you're using the same settings across your app:
```kotlin theme={null}
object NutritionConfig {
val standard = NutritionalAnalysisConfig(
analysisMode = NutritionRecordAnalysisMode.PRECISE,
countryCode = null,
languageCode = null,
includeNutriScore = true,
includeDishDescription = null,
includeIngredients = true,
includeNutritionFields = listOf(
NutritionalField.ENERGY_KCAL,
NutritionalField.PROTEIN_G,
NutritionalField.FAT_TOTAL_G,
NutritionalField.CARBOHYDRATE_G,
NutritionalField.FIBER_TOTAL_DIETARY_G
)
)
}
// Usage
val record = spikeConnection.analyzeNutrition(
image = image,
consumedAt = Instant.now(),
config = NutritionConfig.standard
)
```
## Related Documentation
* [Nutrition AI Overview](/nutrition-ai/overview) — API overview and key features
* [Implementation Guide](/nutrition-ai/implementation) — Detailed API implementation patterns
* [Asynchronous Processing](/nutrition-ai/async) — Webhook configuration and handling
* [Nutritional Fields Reference](/technical-references/nutritional_fields) — Complete list of available nutritional fields
# Android SDK Overview
Source: https://docs.spikeapi.com/sdk-docs/android/overview
Overview of SDK-based integrations with health platforms including common principles, integration creation process, and data extraction methods for mobile health data management.
# Common principles
SDK-based integrations (Apple Health Kit, Android Health Connect, and Samsung Health Data) have a few common principles that separate them from all other providers.
* Data is stored only on the device (phone) therefore, requires to be actively extracted
* Permission control is very gradual (each metric reading requires approval)
* Data becomes available instantly as it's recorded
* Integration requires active management through SDKs
# Creating integrations
Creating these integrations does not follow the OAuth path. Meaning
* there will be no redirecting to the provider authorization page,
* and therefore no callback to URL after permissions are granted.
* Permissions are granted locally, on the phone itself, by triggering SDK methods.
* Permissions are also managed by default OS schemas, application users navigating settings menus, are granted for application package (reinstalling the app might require reauthorization), can be revoked or ignored when requested by OS based on their policies.
The schema below should explain the flow and explain the sequence of events, the moment integration gets created.
# Data extraction
Data stored only on the user's mobile device's local hardware (encrypted at rest). To make data available over API, first you must select and call SDK functions dedicated to sleep, workouts or other metrics reading.
The schema below should explain the flow and explain the sequence of events, the moment when data becomes available for reading over API and SDK.
# Events sequence schema
# Android SDK Setup
Source: https://docs.spikeapi.com/sdk-docs/android/setup
This document provides a setup guide of the Spike SDK for Android platform.
## Version
**Current Android(Kotlin) SDK Version:** `4.7.12`
## Resources
* Android Package: [Available here](https://gitlab.com/spike_api/spike-android-sdk/-/packages/40880707)
* API Reference for Spike SDK: [Available here](https://spike_api.gitlab.io/spike-android-sdk/index.html)
* Example app: [Available here](https://gitlab.com/spike_api/public/spike-sdk-examples/-/tree/master/android-v3?ref_type=heads)
## Requirements
* **Android Version**: `9.0+` (Level 28, P, Pie)
* `SpikeSDK` is compiled using Kotlin 1.9
## Setup Guide
To add the SDK to your project, you have to add the following to your project's `build.gradle` file in the repositories block.
```gradle theme={null}
allprojects {
repositories {
// Other repositories
maven {
url 'https://gitlab.com/api/v4/projects/43396247/packages/maven'
}
}
}
```
After that, add the following to your app's `build.gradle` file in the dependencies block.
```text theme={null}
dependencies {
// Other dependencies
implementation "com.spikeapi.sdk:spike-sdk:4.7.12"
}
```
### Android Permissions
Include the necessary health permissions in your AndroidManifest.xml to fully leverage the Spike SDK and access data from apps integrated with Health Connect. Please refer to [this guide](https://developer.android.com/health-and-fitness/guides/health-connect/get-started#declare-permissions) for details on the required permissions.
**Note**: Only request permissions essential to your app’s functionality. Requesting unused permissions may lead to Play Store rejections.
```xml theme={null}
```
XML extensive permissions
```xml theme={null}
```
Add an intent filter to your activity definition so that you can request the permissions at runtime.
```xml theme={null}
```
To handle Android 14 you also need to add activity-alias to your AndroidManifest.xml It is just a wrapper for the activity that requests permissions so no real activity is necessary.
```xml theme={null}
```
## Compatibility
There is a known incompatibility with Firebase libraries, as their latest versions are compiled using Kotlin 2.
Spike SDK has been tested and works best with Firebase BOM up to version 33.x.x.
# Android SDK Usage Guide
Source: https://docs.spikeapi.com/sdk-docs/android/usage-guide
Start getting Spike data in 3 steps using Spike SDK for Android platform.
## Step 1: Create a Spike Connection
To set up the Spike SDK create `SpikeConnectionV3` instance with your Spike application id, application user id and signature unique to each of your application users (more on generating signatures [here](/api-docs/authentication)):
```kotlin theme={null}
val spikeConnection = SpikeConnectionAPIv3.createConnection(
applicationId = 1000,
signature = "signature",
endUserId = "user-id",
context = context
)
```
## Step 2: Ask User for Permissions
If you want to read data from Android Health Connect, you have to ensure the user gives your app permissions.
First, you have to check if Health Connect is available on users' phone:
```kotlin theme={null}
val hcAvailability = spikeConnection.checkHealthConnectAvailability()
```
where
```kotlin theme={null}
public enum class HealthConnectAvailabilityStatus(public val value: String) {
/**
* The Health Connect SDK is unavailable on this device at the time. This can be due to the
* device running a lower than required Android Version. Apps should hide any integration
* points to Health Connect in this case.
*/
NOT_INSTALLED("NOT_INSTALLED"),
/**
* The Health Connect SDK APIs are currently unavailable, the provider is either not installed
* or needs to be updated. Apps may choose to redirect to package installers to find a suitable
* APK.
*/
UPDATE_REQUIRED("UPDATE_REQUIRED"),
/**
* The Health Connect SDK APIs are available.
*/
INSTALLED("INSTALLED"),
}
```
If an update is required, you can use Spike helper to open Play Store for user to install Health Connect:
```kotlin theme={null}
spikeConnection.openHealthConnectInstallation()
```
If Health Connect is installed, you can get permissions that are needed and a list of already provided permissions:
```kotlin theme={null}
// HC integration has to be enabled in Spike SDK connection before
// using further methods for reading data or managing permissions:
spikeConnection.enableHealthConnectIntegration()
val permissionManager = spikeConnection.getHealthConnectPermissionManager()
val requiredPermissions = permissionManager.getPermissions(
statisticsTypes = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL)
)
val grantedPermissions = permissionManager.getGrantedPermissions()
```
If you have missing permissions, you can ask Android to present the user with a modal asking user for permission to read the data. Example for Compose:
```kotlin theme={null}
val permissionLauncher = rememberLauncherForActivityResult(
permissionManager.getRequestPermissionResultContract(),
onResult = {
}
)
```
Please note that users might only grant partial permissions. In such cases, it’s up to you to decide whether your app can function effectively
with limited access. The Spike SDK itself will still operate even without full permissions; however, it may result in no data being returned in
certain scenarios. Conversely, if your app has been granted additional permissions beyond the minimum required for specific data types, we may
enhance certain entries by incorporating data sourced from other types (e.g., identifying manually entered data).
You can now use `StatisticsFilter(providers = listOf(Provider.HEALTH_CONNECT))` to specifically retrieve data from Health Connect. Alternatively, you can omit the providers parameter entirely and allow Spike to choose the most suitable data source based on your request.
## Step 3: Get Data
The maximum permitted query date range is 90 days
There are four types of data you can retrieve from Spike:
* **[Statistics](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-statistics.html)** are calculated values derived from records.
* **[Activities](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-activities.html)** are data about user's activities or workouts.
* **[Sleep](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-sleep.html)** is data about user's sleep.
* **[Records](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-records.html)** consist of the raw data points collected from user devices or applications.
### Statistics
Get daily statistics for steps and total distance from health Connect:
```kotlin theme={null}
val dailyStatistics = spikeConnection.getStatistics(
types = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL),
from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
to = Instant.now(),
interval = StatisticsInterval.DAY,
filter = StatisticsFilter(providers = listOf(Provider.HEALTH_CONNECT))
)
```
Reference:
* [StatisticType](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-type/index.html)
* [StatisticsInterval](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-interval/index.html)
* [StatisticsFilter](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-filter/index.html)
* [Statistic](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistic/index.html)
### Records
Get all records we have from Garmin provider:
```kotlin theme={null}
val records = spikeConnection.getRecords(
types = setOf(MetricType.STEPS_TOTAL, MetricType.CALORIES_BURNED_TOTAL),
from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
to = Instant.now(),
filter = StatisticsFilter(providers = listOf(Provider.HEALTH_CONNECT))
)
```
Reference:
* [MetricType](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-metric-type/index.html)
* [StatisticsFilter](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-filter/index.html)
* [Record](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-record/index.html)
# Android SDK for Samsung Health
Source: https://docs.spikeapi.com/sdk-docs/android/usage-guide-shd
Start getting Spike data in 3 steps using Spike SDK for Samsung Health Data on Android platform.
## Requirements and Limitations
Samsung Health Data is available on Android devices only!
* Samsung Health Data SDK runs on devices with Android 10 (API level 29) or above. It is available on all Samsung smartphones and non-Samsung Android smartphones.
* Samsung Health Data SDK works with Samsung Health. Samsung Health version 6.30.2 or higher is required.
* Samsung Health Data SDK supports Java 17 or higher version.
* The emulator is not supported.
* Data obtained using Samsung Health Data SDK is for fitness and wellness information only. It is not for the diagnosis or treatment of any medical condition.
## Step 1: Create a Spike Connection
If you already set up [Health Connect integration](/sdk-docs/android/usage-guide) in your app, you should [skip this step](#step-2%3A-ask-user-for-permissions) and use the same `SpikeSDK` connection object.
To set up the Spike SDK create `SpikeConnectionV3` instance with your Spike application id, application user id and signature unique to each of your application users (more on generating signatures [here](/api-docs/authentication)):
```kotlin theme={null}
val spikeConnection = SpikeConnectionAPIv3.createConnection(
applicationId = 1000,
signature = "signature",
endUserId = "user-id",
context = context
)
```
## Step 2: Ask User for Permissions
If you want to read data from Samsung Health, you have to ensure the user gives your app permissions.
First, you have to check if Samsung Health is available on users' phone using
[checkSamsungHealthDataAvailability](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/check-samsung-health-data-availability.html) method:
```kotlin theme={null}
val availability = spikeConnection.checkSamsungHealthDataAvailability(activity = activity)
```
where
```kotlin theme={null}
public data class SamsungHealthDataAvailability(
public val status: SamsungHealthDataAvailabilityStatus,
public val errorCode: Int,
public val message: String
)
public enum class SamsungHealthDataAvailabilityStatus(public val value: String) {
/**
* Samsung Health is not installed. Ask the user to install it.
*/
NOT_INSTALLED("NOT_INSTALLED"),
/**
* The version of Samsung Health is too old. Ask users to update it.
*/
UPDATE_REQUIRED("UPDATE_REQUIRED"),
/**
* The Samsung Health Data is installed but is disabled.
*/
DISABLED("DISABLED"),
/**
* Samsung Health has been installed, but the user didn't perform an initial process, such as
* agreeing to the Terms and Conditions.
*/
NOT_INITIALIZED("NOT_INITIALIZED"),
/**
* Samsung Health returned the other error.
*/
ERROR_OTHER("ERROR_OTHER"),
/**
* Samsung Health Data is available.
*/
INSTALLED("INSTALLED"),
}
```
If Samsung Health is installed, you can ask the user for permissions using [requestPermissions](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-permission-manager/request-permissions.html) method:
```kotlin theme={null}
// Samsung Health integration has to be enabled in Spike SDK connection before
// using further methods for reading data or managing permissions:
spikeConnection.enableSamsungHealthDataIntegration(activity = activity)
spikeConnection.getSamsungHealthDataPermissionManager().requestPermissions(
statisticsTypes = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL)
)
```
Please note that users might only grant partial permissions. In such cases, it’s up to you to decide whether your app can function effectively with limited access.
The Spike SDK itself will still operate even without full permissions; however, it may result in no data being returned in certain scenarios.
You can now use `StatisticsFilter(providers = listOf(Provider.SAMSUNG_HEALTH_DATA))` to specifically retrieve data from Samsung Health. Alternatively, you can omit the
providers parameter entirely and allow Spike to choose the most suitable data source based on your request.
## Step 3: Get Data
There are four types of data you can retrieve from Spike:
* **[Statistics](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-statistics.html)** are calculated values derived from records.
* **[Activities](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-activities.html)** are data about user's activities or workouts.
* **[Sleep](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-sleep.html)** is data about user's sleep.
* **[Records](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-records.html)** consist of the raw data points collected from user devices or applications.
### Statistics
Get daily statistics for steps and total distance from Samsung Health using the [getStatistics](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-statistics.html) method:
```kotlin theme={null}
val dailyStatistics = spikeConnection.getStatistics(
types = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL),
from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
to = Instant.now(),
interval = StatisticsInterval.DAY,
filter = StatisticsFilter(providers = listOf(Provider.SAMSUNG_HEALTH_DATA))
)
```
References:
* [StatisticType](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-type/index.html)
* [StatisticsInterval](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-interval/index.html)
* [StatisticsFilter](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistics-filter/index.html)
* [Statistic](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.datamodels/-statistic/index.html)
### Records
Get all records we have from Samsung Health using the [getRecords](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/get-records.html) method:
```kotlin theme={null}
val records = spikeConnection.getRecords(
types = setOf(MetricType.STEPS_TOTAL, MetricType.CALORIES_BURNED_TOTAL),
from = LocalDate.now().minusWeeks(1).atStartOfDay(ZoneId.systemDefault()).toInstant(),
to = Instant.now(),
filter = StatisticsFilter(providers = listOf(Provider.SAMSUNG_HEALTH_DATA))
)
```
## Background Delivery
Samsung Health data can be delivered in the background the same way as Apple HealthKit or Android Health Connect.
If you want to use background delivery to get Samsung Health data, you have to enable Samsung Health Data integration first:
```kotlin theme={null}
spikeConnection.enableSamsungHealthDataIntegration(activity = activity)
```
After enabling Samsung Health Data integration, you can use background delivery normally.
See the [background delivery section](/sdk-docs/android/background-delivery) for more details.
## Developer Mode for Testing
To test Samsung Health Data integration on your phone, you have to enable developer mode
in the Samsung Health app:
1. Tap the ‘⋮’ button of Samsung Health in the top-right.
2. Select Settings > About Samsung Health.
3. Tap the version line region quickly 10 times or more.
If you are successful, the Developer mode (Samsung Health Data SDK) button is displayed.
4. Select Developer mode (Samsung Health Data SDK) .
5. Agree with the Notice about usage of the Developer mode.
6. To read data from Samsung Health with Samsung Health Data SDK, turn Developer Mode for Data Read on.
After your app is ready for release, you should apply for a Samsung Partnership Agreement. For more information, please contact Spike support.
The Samsung Health developer mode is ONLY intended for testing or debugging your app. It is NOT for app users. Do not provide a developer mode guide to app users.
## See Also
* [Samsung error codes](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-availability/error-code.html)
* [SamsungHealthDataPermissionManager](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-permission-manager/index.html)
* [SamsungHealthDataAvailabilityStatus](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-availability-status/index.html)
* [SpikeConnectionV3.enableSamsungHealthDataIntegration](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/enable-samsung-health-data-integration.html)
* [SpikeConnectionV3.disableSamsungHealthDataIntegration](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/disable-samsung-health-data-integration.html)
* [SpikeConnectionV3.isSamsungHealthDataIntegrationEnabled](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/is-samsung-health-data-integration-enabled.html)
# Flutter SDK Background Delivery
Source: https://docs.spikeapi.com/sdk-docs/flutter/background-delivery
Background delivery ensures that data updates are sent to your backend via webhooks, even when the application is in the background or closed.
## iOS
### Important Notes About Background Delivery on iOS
* For most data types, the most possible frequency of updates is 1 hour.
* iOS can update data more frequently for some data types, for example, vo2 max.
* iOS may throttle the frequency of updates for background delivery depending on the app's activity, battery state, etc.
* Background delivery is not possible while a device is locked, so it will be executed only when the device is unlocked.
* iOS may stop background delivery if it detects that the app is not active for a long time.
* The feature is available starting with iOS 15.
**Important:** The Spike SDK, along with any other HealthKit applications, cannot guarantee data synchronization on a fixed schedule. The hourly sync
interval serves as a guideline rather than a strict requirement enforced by iOS. Consequently, the actual synchronization frequency may vary,
occurring hourly, once per day, or during specific system-defined events, such as the conclusion of Sleep Mode or when the device begins charging.
### Setup
#### Enable Background Delivery for the Application Target
* Open XCode with your ios project
* Open the folder of your project in Xcode
* Select the project name in the left sidebar
* Open the Signing & Capabilities section
* Select HealthKit background delivery under the HealthKit section
#### Initialization at Application Startup
For background delivery to work properly, you need to initialize the Spike SDK at app startup.
`AppDelegate.swift`:
```swift theme={null}
import SpikeSDK
...
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
...
Spike.configure()
...
}
...
```
## Android
### Important Notes About Background Delivery on Android
* Background delivery is scheduled to run every hour, but ultimately, Android decides when the delivery will be executed.
* Android may throttle the frequency of updates for background delivery depending on the app's activity, battery state, etc.
* Android may stop background delivery if it detects that the app is not active for a long time.
* There is a limit of queries that can be done in Health Connect, and it is different for foreground and background reads, so please request only essential data to be delivered in the background. More information in Health Connect documentation.
**Important**: The Spike SDK, along with any other applications, cannot guarantee data synchronization on a fixed schedule. The hourly sync interval serves as a guideline rather than a strict requirement enforced by Android. Consequently, the actual synchronization frequency may vary, occurring hourly, once per day, or during specific system-defined events, such as the conclusion of Sleep Mode or when the device begins charging.
### Setup
Add the following permission to your `AndroidManifest.xml` (for Google Health Connect):
```xml theme={null}
```
## Flutter Specific
Add `includeBackgroundDelivery: true` when asking for Health Connect permissions:
```dart theme={null}
try {
final request = await connection.requestHealthPermissions(
statisticTypes: [StatisticsType.steps],
includeBackgroundDelivery: true,
);
} on SpikeException catch (e) {
log("Error: $e");
}
```
You can also ask for background delivery permission at the same time as other permissions.
Now you can enable background delivery:
```dart theme={null}
try {
await connection.enableBackgroundDelivery(
statisticTypes: [StatisticsType.steps],
sleepConfigs: [
SleepConfig(includeMetricTypes: {MetricType.heartrate})
],
);
} on SpikeException catch (e) {
log("Error: $e");
}
```
Keep in mind that calling `enableBackgroundDelivery()` will overwrite previous configuration. So you have to call it with all the data types you want in one call.
To check current configuration call `getBackgroundDeliveryConfig()` method.
To stop background delivery call `disableBackgroundDelivery()` method.
## Samsung Health
If Samsung Health Data is enabled before enabling background delivery, `enableBackgroundDelivery` will automatically enable Samsung Health Data background delivery.
# Flutter SDK Changelog
Source: https://docs.spikeapi.com/sdk-docs/flutter/changelog
A changelog documenting version updates and changes of Spike SDK for Flutter platform.
## 4.5.24
* Updated Android SDK to `4.5.22`
* Updated iOS SDK to `4.5.21`
* Added **backfill** on the v3 connection to upload historical health data for a chosen number of days. Call it after you have granted the relevant permissions (HealthKit on iOS; Health Connect and/or Samsung Health Data on Android).
```dart theme={null}
await spikeConnection.backfill(
statisticTypes: statisticTypes,
metricTypes: metricTypes,
activityConfigs: [activityConfig],
sleepConfigs: [sleepConfig],
);
```
* **iOS (HealthKit):** added `requestPermissionsFromHealthKitAndBackfill` — requests read permissions for the given categories, then starts a backfill. Data for today and yesterday is pushed before the call resolves; older days continue in the background.
```dart theme={null}
await spikeConnection.requestPermissionsFromHealthKitAndBackfill(
statisticTypes: statisticTypes,
metricTypes: metricTypes,
activityConfigs: [activityConfig],
sleepConfigs: [sleepConfig],
);
```
* **Android (Samsung Health Data):** added `requestPermissionsFromSamsungHealthDataAndBackfill` — enables Samsung Health Data integration, requests permissions for the given categories, then starts a backfill.
* **Android (Health Connect):** added `requestPermissionsFromHealthConnectAndBackfill` — enables Health Connect integration, requests permissions for the given categories, then starts a backfill.
* **iOS:** If after updating you get errors while building iOS app please do following:
1. `rm -rf ios/Pods/ ios/Podfile.lock` - delete old pods
2. `cd ios; pod install --repo-update` - install updated pods
3. Open project in Xcode and do a full clean: `Product -> Clean build folder` or `Shift + Command + K`
## 4.5.14
* Added `RecordConfig` for configuring records queries:
* `includeSamples` - whether to include samples (raw data points) in the response
* Updated `getRecords` to accept an optional `RecordConfig` parameter:
```
spikeConnection.getRecords(
ofTypes: metricTypes,
from: startDate,
to: endDate,
filter: StatisticsFilter(excludeManual: true),
config: RecordConfig(includeSamples: true),
)
```
* Fixed ActivityConfig parsing
* Updated Android SDK to `4.5.12`
* Updated iOS SDK to `4.5.11`
* IMPORTANT! If after updating you get errors while building iOS app please do following:
1. `rm -rf ios/Pods/ ios/Podfile.lock` - delete old pods
2. `cd ios; pod install --repo-update` - install updated pods
3. Open project in XCode 26 and do a full clean: `Product -> Clean build folder` or `Shift + Command + K`
## 4.4.14
* Updated Android SDK to `4.4.12`
* Updated iOS SDK to `4.4.11`
* Updated `Unit` enum with new values:
* Added: `miles`, `mPerSec`, `fahrenheit`, `ms`, `seconds`, `degrees`, `sleepStage`, `secPerM`, `rpm`, `spm`, `breathsPerMin`, `g`, `lbs`, `st`, `mmHg`, `mLPerKgPerMin`, `uV`, `mgPerDl`, `w`
* Removed: `kmh`, `kmPerMin`
* Improved data communication with API
* Enhanced the `getSleep` endpoint.
Important:
* Dates provided are now interpreted as dates only; the time component is ignored when querying the API.
* The primary night sleep period is associated with the calendar date on which it ends.
## 4.3.184
* Updated Android SDK to `4.3.142`
* Internal improvements
* Updated iOS SDK to `4.3.161`
* Better Nutrition Facts Label Recognition response parsing
## 4.3.174
* MetricType: added namespace with metric allowlists for API requests:
* `MetricType.availableForRecordsRequest` – list of metric types available for records requests
* `MetricType.availableForActivitiesRequest` – list of metric types available for activities requests
* `MetricType.availableForSleepRequest` – list of metric types available for sleep requests
* `MetricType.availableForPersonRequest` – list of metric types available for person requests
* Updated iOS SDK to `4.3.151`:
* Improved data push to API
* Fixed workout digest generation for caching
* Improved client cache configuration
## 4.3.164
* Updated Android SDK to `4.3.132`:
* Samsung Health Data Integration - Major Expansion:
* Added support for reading and pushing the following data types from Samsung Health:
* Workouts (exercises)
* Sleep data
* Body composition data (weight, height, BMI, body fat)
* User profile data (weight, height, birth date, gender)
* Blood pressure measurements
* Skin temperature measurements
* Heart rate measurements
* Blood oxygen saturation (SpO2) measurements
* Body temperature measurements
* More efficient Samsung Health Data pushes
* Large Samsung Health Data pushes are now automatically split into smaller chunks for reliable delivery
* Large Health Connect pushes are now automatically split into smaller chunks for reliable delivery
## 4.3.154
* Added Nutrition Facts Label Recognition for extracting nutritional information from label photos.
For more information please visit our [documentation](https://docs.spikeapi.com).
* `recognizeNutritionFactsLabel({required String imageBase64, NutritionFactsLabelRecognitionConfig? config})` - Analyze a nutrition facts label image and extract nutritional data
* Added methods for manual nutrition record management:
* `createNutritionRecord(NutritionRecord nutritionRecord)` - Insert a new nutrition record with custom data
* `replaceNutritionRecord(NutritionRecord nutritionRecord)` - Replace an existing nutrition record completely
* Added `NutritionRecordUtils` for creating and manipulating nutrition records:
* `NutritionRecordUtils.create({...})` - Create a new nutrition record with specified properties
* `record.withIngredient(ingredient)` - Add an ingredient to a record
* `record.withoutIngredient(ingredient)` - Remove an ingredient from a record
* `record.withServingSize(servingSize, unit)` - Update serving size and unit
* Added `NutritionRecordIngredientUtils` for creating and manipulating ingredients:
* `NutritionRecordIngredientUtils.create({...})` - Create a new nutrition record ingredient
* `ingredient.withNutritionalField(nutritionalField, value)` - Set a nutritional field value
* `ingredient.withoutNutritionalField(nutritionalField)` - Remove a nutritional field
* `ingredient.withScaledServingSize(newServingSize)` - Scale serving size and nutritional values proportionally
* Nutrition AI methods now return `NutritionRecordAnalysisResult` instead of `NutritionRecord`.
This separates processing metadata (status, failure reason) from the actual nutrition data.
The following methods are affected:
* `analyzeNutrition()` → returns `Future`
* `getNutritionRecords()` → returns `Future>`
* `getNutritionRecord()` → returns `Future`
* `updateNutritionRecordServingSize()` → returns `Future`
* `createNutritionRecord()` → returns `Future`
* `replaceNutritionRecord()` → returns `Future`
The `NutritionRecordAnalysisResult` contains:
* `recordId` - The record ID
* `status` - Processing status (pending, processing, completed, failed, updated)
* `failureReason` - Reason for failure (if status is failed)
* `uploadedAt` - Upload timestamp
* `nutritionRecord` - The actual `NutritionRecord` (null if status is not completed/updated)
* Update Android SDK to `4.3.122`
* Update iOS SDK to `4.3.141`
* IMPORTANT! If after updating you get errors while building iOS app please do following:
1. `rm -rf ios/Pods/ ios/Podfile.lock` - delete old pods
2. `cd ios; pod install --repo-update` - install updated pods
3. Open project in XCode 26 and do a full clean: `Product -> Clean build folder` or `Shift + Command + K`
## 4.3.144
* Added Nutrition AI features for analyzing food images. For more information please visit our [documentation](https://docs.spikeapi.com).
* `analyzeNutrition(imageBase64, consumedAt, config)` - Analyze food image synchronously and wait for results
* `submitNutritionForAnalysis(imageBase64, consumedAt, config)` - Analyze food image asynchronously, returns record ID
* `getNutritionRecords(from, to)` - Retrieve nutrition records for a date range
* `getNutritionRecord(id)` - Get a specific nutrition record by ID
* `deleteNutritionRecord(id)` - Delete a nutrition record
* `updateNutritionRecordServingSize(id, servingSize)` - Update serving size for a nutrition record
* Added new `Provider`:
* luna
* Added new `ProviderSource` types:
* fitbitSkinTemperatureSummary
* garminWellnessSkinTemperatureSummary
* lunaSleep
* Removed `ProviderSource` types:
* fitbitUserActivitiesList
* fitbitUserActivitiesDate
* fitbitUserActivitiesHeartDate
* fitbitUserActivitiesHeartDateInterday
* withingsMeasureGetWorkouts
* withingsMeasureGetActivity
* withingsMeasureGetMeas
* Removed `ActivityTag`:
* onBicycle
* Removed `Provider`:
* wahoo
## 4.3.134
* Update Android SDK to `4.3.102`
* Enable Health Connect integration in admin console right after enabling integration
* Enable Samsung Health Data integration in admin console right after enabling integration
* Update iOS SDK to `4.3.121`
* Enable HealthKit integration in admin console right after requesting permissions
## 4.3.124
* Added ability to read mindfulness activities:
```
spikeConnection.getActivities(
config: ActivityConfig(activityCategories: {ActivityCategory.mindfulness})
from: dateFrom,
to: dateTo
)
```
* Added ability to check Android Health Connect features availability:
```
const isMindfulessAvailable = spikeConnection.isHealthConnectFeatureAvailable(HealthConnectFeature.FEATURE_MINDFULNESS_SESSION)
```
* Added new ActivityTag types:
* mindfulness
* Added new ActivityType types:
* ecgMeasurement
* triathlon
* biathlon
* duathlon
* rollerblading
* skateboarding
* skating
* calisthenics
* weightLifting
* canoeing
* floorball
* jiuJitsu
* diving
* orienteering
* bootcamp
* motorsports
* horseriding
* paragliding
* multisport
* bloodTest
* mindfulnessSession
* Added new ProviderSource types:
* appleHealthkitMindfulness
* healthConnectMindfulnessSession
* Added a new ActivityCategory type:
* mindfulness
* Updated Android SDK to `4.3.92`
* Updated Health connect to `1.1.0-rc03`
* Updated compileSdk to `36`
* Updated gradle to `8.13`
* `android.permission.health.READ_MINDFULNESS` user permission should be added to `AndroidManifest.xml` file
* Updated iOS SDK to `4.3.111`
## 4.3.114
* Updated Android SDK to `4.3.82`:
* Workaround for the Android 15 bug to make background delivery more resilient
* More efficient pushes to Spike API
## 4.3.104
* Updated Android SDK to `4.3.72`:
* Allow reading `SPO2` and `SWIMMING_LENGTHS` in `getActivities`
* Allow reading `SPO2` and `SKIN_TEMPERATURE` in `getSleeps`
* Updated iOS SDK to `4.3.101`:
* Improved how sleep data is read for sleep score
## 4.3.94
* Updated Android SDK to `4.3.62`:
* Optimized background delivery
## 4.3.84
* Added `.coros` `Provider`
* Updated `ProviderSource` enum
* Added statistics: `stressScore`, `recoveryScore`, `activityScore`
* Updated Android SDK to `4.3.52`:
* Added `Provider.COROS`
* Updated `ProviderSource` enum
* Added new Statistic types:
* STRESS\_SCORE
* RECOVERY\_SCORE
* ACTIVITY\_SCORE
* Added new Metric types for `getSleep` request:
* SLEEP\_DURATION
* SLEEP\_DURATION\_AWAKE
* SLEEP\_DURATION\_DEEP
* SLEEP\_DURATION\_LIGHT
* SLEEP\_DURATION\_NAP
* SLEEP\_DURATION\_REM
* SLEEP\_EFFICIENCY
* SLEEP\_INTERRUPTIONS
* SLEEP\_LATENCY
* SLEEP\_SCORE
* Updated iOS SDK to `4.3.91`:
* Added `.coros` `Provider`
* Updated `ProviderSource` enum
* Improved the way data is sent to API to reduce the size and speed of calls to Spike
* Added new Statistic types:
* stressScore
* recoveryScore
* activityScore
* Added new Metric types for `getSleep` request:
* sleepDuration
* sleepDurationAwake
* sleepDurationDeep
* sleepDurationLight
* sleepDurationNap
* sleepDurationRem
* sleepEfficiency
* sleepInterruptions
* sleepLatency
* sleepScore
* Better keychain compatibility
NOTE: If you use the keychain in your app with a service name equal to your bundle
identifier, please check if it contains key named `spikeApiToken` and delete it.
## 4.3.74
* Added new `SpikeSDKV3.setLogCallback()` method to receive logs from the native SDK.
Example usage:
```dart theme={null}
await SpikeSDKV3.setLogCallback(
callback: (level, message) {
print("[SpikeSDK.${level.toJson()}] $message");
},
);
```
* Added a new metric type:
* `glucose`
* Updated Android SDK to `4.3.42`:
* New `MetricType.GLUCOSE` available in both Health Connect and Samsung Health
* Update Samsung Health Data SDK to version 1.0.0 (available only on Android 29+)
* Updated iOS SDK to `4.3.81`:
* Added `MetricType`: `.glucose`
## 4.3.64
* Updated Android SDK to `4.3.93`:
* Deprecated the old Spike `SpikeSDK`
* Switch to protobuf java-lite 3.25.5 for better compatibility with firebase
* Updated iOS SDK to `4.3.71`:
* Deprecated the old Spike `SpikeSDK`
* Added `disableHealthKitIntegration` and `isHealthKitIntegrationEnabled` functions in spike connection
## 4.3.54
* Added new providers added: dexcom, freestyleLibre, huawei, strava
* Added .distanceSwimming into the list of metrics available in activities
* Updated native iOS SDK Version to `4.3.61`
* Updated native Android SDK Version to `4.3.22`
## 4.3.44
* Added Samsung Health Integration on Android
* Updated native Android SDK Version to `4.3.12`:
* New statistics for Health Connect:
* HEARTRATE
* HEARTRATE\_MAX
* HEARTRATE\_MIN
* New metrics for Health Connect:
* HEARTRATE
* HEARTRATE\_MAX
* HEARTRATE\_MIN
* Improved transport protocol for even faster requests to Spike API
* Added Samsung Health Integration for:
* `StatisticType`s:
* STEPS
* DISTANCE\_TOTAL
* CALORIES\_BURNED\_ACTIVE
* CALORIES\_BURNED\_TOTAL
* CALORIES\_BURNED\_BASAL
* Added new `Provider`:
* SAMSUNG\_HEALTH\_DATA
* Added new `ProviderSource`:
* SAMSUNG\_HEALTH\_DATA\_AGGREGATION
* Renamed `MetricType`s:
* SLEEP\_BREATHING\_RATE to BREATHING\_RATE
* SLEEP\_BREATHING\_RATE\_MIN to BREATHING\_RATE\_MIN
* SLEEP\_BREATHING\_RATE\_MAX to BREATHING\_RATE\_MAX
* Updated native iOS SDK Version to `4.3.51`:
* Added new `Provider`:
* samsungHealthData
* Added new `ProviderSource`:
* samsungHealthDataAggregation
* Renamed `MetricTypes`:
* `sleepBreathingRate` to `breathingRate`
* `sleepBreathingRateMin` to `breathingRateMin`
* `sleepBreathingRateMax` to `breathingRateMax`
## 4.3.34
* Updated native iOS SDK Version to `4.3.41`
* IntegrationInitConfig is now codable
* Additional check in `getIntegrationInitUrl` for email in ultrahuman integration
* Updated native Android SDK Version to `4.2.82`
* Added `IntegrationInitConfigUtils` for usage in Flutter and React Native libraries
* Additional check in `getIntegrationInitUrl` for email in ultrahuman integration
* New provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider: .ultrahuman, config: IntegrationInitConfig(providerUserIdentifier: "user@mail.com"))
```
## 4.3.24
* Updated native iOS SDK Version to `4.3.31`
* Improve how statistics are read from HealthKit
* New metric types:
* bodyFat
* bodyFatMax
* bodyFatMin
* bodyBoneMass
* bodyMassIndex
* bloodPressureSystolic
* bloodPressureSystolicMax
* bloodPressureSystolicMin
* bloodPressureDiastolic
* bloodPressureDiastolicMax
* bloodPressureDiastolicMin
* New fields in `UserProperties`:
* bodyBoneMass
* bodyFat
* bodyMassIndex
* Updated `getIntegrationInitUrl`. Now it accepts `IntegrationInitConfig` config object where you can pass:
* `redirectUri`: will override the one set in admin console
* `state`: when the authorization server redirects back to the client, it includes the `state` value originally sent
* `providerUserIdentifier`: at the moment used (and required) only when integrating with ultrahuman
* New provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider: .ultrahuman, config: IntegrationInitConfig(providerUserIdentifier: "user@mail.com"))
```
* Improved transport protocol for background delivery
* Updated native Android SDK Version to `4.2.72`
* Added new `MetricType`s:
* BODY\_FAT
* BODY\_FAT\_MAX
* BODY\_FAT\_MIN
* BODY\_BONE\_MASS
* BODY\_MASS\_INDEX
* BLOOD\_PRESSURE\_SYSTOLIC
* BLOOD\_PRESSURE\_SYSTOLIC\_MIN
* BLOOD\_PRESSURE\_SYSTOLIC\_MAX
* BLOOD\_PRESSURE\_DIASTOLIC
* BLOOD\_PRESSURE\_DIASTOLIC\_MIN
* BLOOD\_PRESSURE\_DIASTOLIC\_MAX
* New fields in `UserProperties`:
* BODY\_FAT
* BODY\_BONE\_MASS
* BODY\_MASS\_INDEX
* Updated `getIntegrationInitUrl`. Now it accepts `IntegrationInitConfig` config object where you can pass:
* `redirectUri`: will override the one set in admin console
* `state`: when the authorization server redirects back to the client, it includes the `state` value originally sent
* `providerUserIdentifier`: at the moment used (and required) only when integrating with ultrahuman
* Added new provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide Ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider = Provider.ULTRAHUMAN, config = IntegrationInitConfig(providerUserIdentifier = "user@mail.com"))
```
## 4.3.14
* Updated native iOS SDK Version to `4.3.11`
* Improved transport protocol for even faster requests to Spike API
* Added new statistic metrics: heartrate, heartrateMax, heartrateMin, heartrateResting
* Updated native Android SDK Version to `4.2.62`
* Added new statistic metrics:
* HEARTRATE\_RESTING
* SLEEP\_DURATION\_TOTAL
* Added new statistic metrics (only from non-HealthConnect providers):
* HEARTRATE
* HEARTRATE\_MIN
* HEARTRATE\_MAX
* Metric types updates
* Better proguard settings for uniqueness of generated class names
* Fixed statistics when in different time zones
* Added consumer proguard rules
## 4.2.74
* Removed outdated example. Please follow [Flutter SDK Usage Guide](/sdk-docs/flutter/usage-guide#step-3%3A-get-data).
## 4.2.64
* Updated readme file
## 4.2.54
* Updated native iOS SDK Version to iOS 4.2.41
* Fixed the date format in JSON push
## 4.2.44
* Fixed JSON parsing of user info response
## 4.2.34
* Added new metrics:
* spo2
* bodyTemperature
* skinTemperature (available only on getSleep())
* sleepBreathingRate (available only on getSleep())
* sleepBreathingRateMin (available only on getSleep())
* sleepBreathingRateMax (available only on getSleep())
* sleepBreathingRateAvg (available only on getSleep())
* Updated native iOS SDK Version to iOS 4.2.31
* Updated native Android SDK Version to 4.2.42
* Added sleepScore property in the Record type
## 4.2.24
* Added new metric: `VO2MAX` available in `getRecords` and `getActivities`
* Changed hrv metric names: `hrvRmssd`, `hrvSdnn`
* Added new statistic: `sleepScore`
* Updated native iOS SDK Version to iOS 4.2.21
* Added new `getUserProperties` method for reading: weight, height, timezone, birthdate, gender
* Updated native Android SDK Version to 4.2.32
* Added new `getUserProperties` method for reading: weight, height, timezone from Health Connect
* Added new `getUserProperties` method for reading: birthdate, gender from other providers
## 4.2.14
* Updated native Android SDK Version to 4.2.12
* Updated native iOS SDK Version to 4.2.11
* 🔴 Breaking Change: Update to `SpikeSDKV3.createConnection`. The method
`Spike.createConnectionAPIv3({appId:, authToken:, customerEndUserId:})` has been updated to:
`SpikeSDKV3.createConnection(applicationId:, signature:, endUserId:)`
* The `applicationId` parameter must now be provided as an **`int`**.
* The `signature` parameter now requires an **`HMAC-SHA256` signed user ID**.
* ⚠️ **Security Notice:**
* **Do not store your HMAC signing key within the application itself**, as this poses a security risk.
* Instead, generate and provide the signature from your backend.
* **Legacy Support**: For **development purposes only**, the previous connection flow remains available under the renamed method:
`SpikeSDKV3.createConnectionLegacy(appId:, authToken:, customerEndUserId:)`
## 4.1.14
* Background delivery for both iOS and Android
* Added triathlon activity type
* Better error messages in iOS SpikeSDK wrapper
* Updated native Android SDK Version to 4.1.12
* Add background delivery (see documentation for more information)
* Health Connect library updated to 1.1.0-alpha11
* Compile SDK and target SDK updated to 35
* Updated kotlin version to 1.9.25
* Updated other dependencies
* Updated gradle to 8.8.0
* Updated native iOS SDK Version to 4.1.11
* Added background delivery
* Implemented sending more sleep data
## 4.0.14
* Completely new SDK! Please see our official documentation for more details and usage instructions
* Updated native Android SDK Version to 4.0.22
* Updated native iOS SDK Version to 4.0.12
* Added getIntegrationInitUrl method to SpikeConnectionV3: generates integration url for the given provider
## 1.3.3
* Android: Fixed build issue on AGP8+
## 1.3.2
* Updated native Android SDK Version to 3.1.4
* Added more raw data to calories request
## 1.3.1
* Updated native iOS SDK Version to 2.4.2:
* Added customer user id validation
* Updated native Android SDK Version to 3.1.1:
* Better way to authenticate with API
* Added saved session validation before the connection is established
* Added customer user id validation
## 1.3.0
* Added ECG handling in iOS.
* Added empty results when ECG is asked for on an Android device.
* Made connections array in iOS thread-safe.
* Updated structure of `SpikeSource` data model.
* README updated with ECG data type.
* Updated native iOS SDK Version to 2.4.1:
* Fix the timezone in case it changes while the app is running.
* Better way to authenticate with API.
* ECG support.
* Add trigger property to extractAndPostData request.
* Add cycling parameters to Activities stream data request.
## 1.2.10
* Updated native Android SDK Version.
* Updated native iOS SDK Version.
* README updated.
## 1.2.9
* Updated native iOS SDK Version.
## 1.2.8
* Updated native Android SDK Version.
* Updated native iOS SDK Version.
* PROD/DEV environments separation added.
* Background delivery handling is documented for the iOS.
* Method to disable background delivery introduced.
* setEnvironment method removed as environment must be set when creating the connection.
## 1.2.7
* Updated native Android SDK Version.
* Updated native iOS SDK Version.
## 1.2.6
* Updated native Android SDK Version.
* Updated native iOS SDK Version.
## 1.2.5
* SpikeStepData class name renamed into SpikeStepsData to keep aligned with Dart coding conventions.
* Intraday steps data introduced.
## 1.2.4
* Updated native Android SDK Version.
## 1.2.3
* Updated native Android SDK Version.
## 1.2.2
* Updated native Android SDK Version
## 1.2.1
* Updated native Android SDK Version
## 1.2.0
* Updated README with Android 14 set-up guide
* Ensured permissions are checked and requested when the user tries to extract the data
* Introduced ensurePermissionsAreGrantedV2 method, so it would become possible to get more information about what happened when requesting for permissions, which might be useful for Flutter Android apps
* Improved processing of the Health Connect permissions when a user cancels giving permissions, gives them partially, or performs some other unexpected behavior
* Updated native iOS SDK Version
## 1.1.1
* Android Native SDK version updated
## 1.1.0
* Updated iOS Native SDK version
* Updated Android Native SDK version
* Recreated Android Native SDK permissions handling
## 1.0.27
* Updated iOS Native SDK version
## 1.0.26
* Updated iOS Native SDK version
## 1.0.25
* Updated iOS Native SDK version
* Updated Android Native SDK version
* Added the method `isPackageInstalled` to Android
## 1.0.24
* Updated iOS Native SDK version
## 1.0.23
* Updated iOS Native SDK version
* Updated Android Native SDK version
* Added more information on exceptions thrown in the README
## 1.0.22
* Updated iOS Native SDK version
## 1.0.21
* Updated iOS Native SDK version
## 1.0.20
* Updated native Android SDK version usage
* Made it possible to set the environment when using Android SDK
## 1.0.19
* Added the method `getSpikeEndUserId` into the `SpikeConnection`
## 1.0.18
* Updated Android SDK integration
## 1.0.17
* Removed pack, unpack, since it is clients who need to manage their state
* Integrated web hook connections into `SpikeConnection`
* Reduced minimum SDK version to 28
* Reduced compile an SDK version to 33
* Updated examples
* Updated README
* Fully changed the integration with Android SDK
## 1.0.16
* Implemented returning boolean after requesting permissions.
## 1.0.15
* Native wrapping with native Android SDK release build issues resolved
* Resolved requesting permissions issues using native Android SDK
* Updated README
* Updated examples
## 1.0.14
* Native wrapping with native Android SDK finalized
## 1.0.13
* Introduced native wrapping with native Android SDK
* Introduced native wrapping with native iOS SDK
## 1.0.12
* Min iOS version reduced for the sleep data
## 1.0.11
* Updated README
* Updated examples
## 1.0.10
* Updated README
* Ensured dart doc is working
## 1.0.9
* Updated README
* Made sure that casting when using connection.extractData() is no longer needed
## 1.0.8
* Updated README
* Updated data keys expectations per changed backend
## 1.0.7
* iOS workouts reading problems resolved
* Made sure floors are added from the iOS data
* Added functionality that allows configuration of the background delivery tasks
* Added debouncing for the background delivery tasks of five seconds, so they would not trigger unnecessary sending to the backend
* Fixed an issue of reading sleep data. Ensured user is requested for additional permissions that are required to collect the statistics needed regarding sleep
* Made sure that in sleep statistics zeroes would be changed into null when no data is read
* Updated README with the background delivery example information
## 1.0.6
* Breathing data issues resolved
* Workouts reading issues resolved
* Updated README
* Updated SpikeSDK so that SpikeSDK.initialize() would not be needed as per documentation
## 1.0.5
* Better background delivery configuration into SpikeSDK added
## 1.0.4
* Improved logging
* Improved sending data to the backend
* Android Health Connect Core recreated using direct API for the Health Connect due to the failure of the Flutter package to properly convert data under the release mode
## 1.0.3
* Added more logging
* Fixed IDs usage
## 1.0.2
* Improved logging
## 1.0.1
* Fully rewritten of the communication with the backend
* Introduced reading the body data
* Improved calculation of time
* Added more logging
## 1.0.0
* Complete rewrite of the library to follow the Spike standards required
## 0.0.10
* Sleep data reading and sending support introduced
## 0.0.9
* Activity groups support introduced
## 0.0.8
* Removed some data for summary
* Workouts data times rounding added, so conversions would not fail
## 0.0.7
* Updated examples
* Summary data reading improved even further
* Added the way to send separate identifier data
* Updated README
## 0.0.6
* Introduced workout data reading
* Improved reading of summary data, and now all the summary data can be read
## 0.0.5
* Background delivery now takes the last three days for the data to be sent
* Removed calories, and basal energy burned introduced instead
## 0.0.4
* Background delivery support introduced
* Better code organization regarding storing some configured data in the storage
* Background delivery interval support introduced
* Improvements regarding date and time resolution when resolving or sending the data
## 0.0.3
* Made sure background tasks would take correct intervals to send the data
* Added more information to logging
* Made sure events logged would be sorted by the date in ascending order
## 0.0.2
* Improved handling of the background tasks registration
* Support to set custom task ID introduced to check if background tasks actually work
* Support for event tracking introduced. Tracking can be enabled or disabled
## 0.0.1
* Apple HealthKit data reading functionality with the ability to send this data to the server of your choice
# Flutter SDK Logging
Source: https://docs.spikeapi.com/sdk-docs/flutter/logging
Getting logs for troubleshooting and debugging from Spike SDK for Flutter platform.
## Code Examples
To receive logs from Spike SDK, use the following code:
```dart theme={null}
await SpikeSDKV3.setLogCallback(
callback: (level, message) {
log("[SpikeSDK.${level.toJson()}] $message");
},
);
```
Callback will get log level enum and string with a log message.
```
enum LogLevel {
verbose,
debug,
warning,
error;
}
```
# Flutter SDK Nutrition AI
Source: https://docs.spikeapi.com/sdk-docs/flutter/nutrition-ai
Analyze food images and retrieve nutritional information using AI-powered analysis in your Flutter app.
## About
The Spike SDK provides a convenient interface for the [Nutrition AI API](/nutrition-ai/overview), allowing you to analyze food images directly from your Flutter application. The SDK handles image encoding, API communication, and response parsing, making it easy to integrate nutritional analysis into your app.
All Spike SDK async methods return `Future` objects. Use `try-catch` blocks or `.catchError()` handlers for error handling. See [Error Handling](#error-handling) for details.
## Key Features
* **AI-Powered Analysis** — advanced computer vision for food identification and nutritional calculations
* **Flexible Processing** — choose between synchronous (wait for results) or asynchronous (background) processing
* **Base64 Support** — submit images as base64-encoded strings
* **Complete Record Management** — retrieve, update, and delete nutrition records
## Available Methods
| Method | Description |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `analyzeNutrition(imageBase64:, consumedAt:, config:)` | Submit food image for synchronous processing and wait for the analysis results |
| `submitNutritionForAnalysis(imageBase64:, consumedAt:, config:)` | Submit food image for asynchronous processing and get record ID immediately for polling afterwards |
| `getNutritionRecords(from:, to:)` | Retrieve nutrition records for a datetime range |
| `getNutritionRecord(id:)` | Get a specific nutrition record by ID |
| `updateNutritionRecordServingSize(id:, servingSize:)` | Update serving size for a nutrition record |
| `deleteNutritionRecord(id:)` | Delete a nutrition record by ID |
## Analyzing Food Images
### Synchronous Processing
Use synchronous analysis when you want to wait for the complete nutritional analysis before proceeding. This is ideal for scenarios where you need immediate results and can display a loading indicator.
```dart theme={null}
import 'dart:convert';
import 'dart:io';
import 'package:spike_flutter_sdk/spike_flutter_sdk.dart';
// Capture image from camera or gallery and convert to base64
final File imageFile = // ... captured from camera or gallery
final bytes = await imageFile.readAsBytes();
final imageBase64 = base64Encode(bytes);
try {
final record = await spikeConnection.analyzeNutrition(
imageBase64: imageBase64,
consumedAt: DateTime.now(),
config: NutritionalAnalysisConfig(
analysisMode: NutritionRecordAnalysisMode.precise,
countryCode: 'us',
languageCode: 'en',
includeNutriScore: true,
includeDishDescription: true,
includeIngredients: true,
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.fatTotalG,
NutritionalField.carbohydrateG,
],
),
);
print('Dish: ${record.dishName ?? "Unknown"}');
print('Serving size: ${record.servingSize ?? 0} ${record.unit?.toJson() ?? "g"}');
print('Calories: ${record.nutritionalFields?["energy_kcal"] ?? 0}');
} catch (e) {
print('Analysis failed: $e');
}
```
You can also call with minimal parameters (config is optional):
```dart theme={null}
// Using defaults - only imageBase64 is required
final record = await spikeConnection.analyzeNutrition(
imageBase64: imageBase64,
);
```
**Processing Time**: Synchronous processing takes some time depending on image complexity. Consider showing a loading indicator to users. If you see that the analysis is taking too long, the recommendation is to use asynchronous processing instead.
### Asynchronous Processing
Use asynchronous processing when you want an immediate response without waiting for the analysis to complete. Record ID is returned.
The image is processed in the background, and you can retrieve results later by requesting nutrition analysis using the record ID or receive them via webhook.
```dart theme={null}
try {
// Submit image for background processing
final recordId = await spikeConnection.submitNutritionForAnalysis(
imageBase64: imageBase64,
consumedAt: DateTime.now(),
config: NutritionalAnalysisConfig(
analysisMode: NutritionRecordAnalysisMode.fast,
countryCode: null,
languageCode: null,
includeNutriScore: null,
includeDishDescription: null,
includeIngredients: true,
includeNutritionFields: null,
),
);
print('Analysis started. Record ID: $recordId');
// Optionally, poll for results later
// Your backend will also receive a webhook when analysis completes
} catch (e) {
print('Failed to submit: $e');
}
```
#### Retrieving Results Asynchronously
After submitting an image for asynchronous processing, you can retrieve the results using the record ID. Check the processing status for completion success.
```dart theme={null}
// Check the status and get results
final record = await spikeConnection.getNutritionRecord(id: recordId);
if (record != null) {
switch (record.status) {
case NutritionRecordStatus.completed:
print('Analysis complete: ${record.dishName ?? "Unknown"}');
break;
case NutritionRecordStatus.processing:
print('Still processing...');
break;
case NutritionRecordStatus.pending:
print('Queued for processing...');
break;
case NutritionRecordStatus.failed:
print('Analysis failed: ${record.failureReason ?? "Unknown error"}');
break;
case NutritionRecordStatus.unknown:
print('Unknown status');
break;
}
}
```
For real-time notifications, configure webhooks in your [admin console](https://admin.spikeapi.com/). Your backend will receive a webhook notification when the analysis completes. See [Asynchronous Processing](/nutrition-ai/async) for webhook implementation details.
## Configuration Options
Customize the analysis using `NutritionalAnalysisConfig`:
```dart theme={null}
final config = NutritionalAnalysisConfig(
// Analysis speed vs. precision
analysisMode: NutritionRecordAnalysisMode.precise, // precise (default) or fast
// Country ISO 3166-1 alpha-2 code in lowercase
countryCode: 'us',
// Language ISO 639-1 code in lowercase
languageCode: 'en',
// Include Nutri-Score rating (A-E)
includeNutriScore: true,
// Include dish description
includeDishDescription: true,
// Include detailed breakdown of ingredients
includeIngredients: true,
// Specify which nutritional fields to include
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.fatTotalG,
NutritionalField.carbohydrateG,
NutritionalField.fiberTotalDietaryG,
NutritionalField.sodiumMg,
],
);
final record = await spikeConnection.analyzeNutrition(
imageBase64: imageBase64,
consumedAt: DateTime.now(),
config: config,
);
```
### `NutritionalAnalysisConfig`
```dart theme={null}
class NutritionalAnalysisConfig {
/// A preferred mode for the analysis. Default is precise.
final NutritionRecordAnalysisMode? analysisMode;
/// Country ISO 3166-1 alpha-2 code in lowercase
final String? countryCode;
/// Language ISO 639-1 code in lowercase
final String? languageCode;
/// Include nutri-score label of the food. Default is false.
final bool? includeNutriScore;
/// Include dish description of the food. Default is false.
final bool? includeDishDescription;
/// Include ingredients of the food. Default is false.
final bool? includeIngredients;
/// Include specific nutrition fields in the analysis report.
/// By default, carbohydrate_g, energy_kcal, fat_total_g and protein_g will be included.
final List? includeNutritionFields;
}
```
### Analysis Modes
```dart theme={null}
enum NutritionRecordAnalysisMode {
fast,
precise
}
```
| Mode | Description |
| --------- | ---------------------------------------------------------------------------- |
| `precise` | Uses advanced AI models for highest accuracy and detailed analysis (default) |
| `fast` | Uses optimized models for quicker processing with good accuracy |
### Default Nutritional Fields
If `includeNutritionFields` is not specified, only these basic fields are included:
* `energyKcal`
* `proteinG`
* `fatTotalG`
* `carbohydrateG`
See [Nutritional Fields Reference](/technical-references/nutritional_fields) for all available fields.
## Managing Nutrition Records
### List Records by Date Range
Retrieve all nutrition records within a specified date range:
```dart theme={null}
final now = DateTime.now();
final startDate = now.subtract(const Duration(days: 7));
final endDate = now;
try {
final records = await spikeConnection.getNutritionRecords(
from: startDate,
to: endDate,
);
for (final record in records) {
final consumedAt = record.consumedAt?.toString() ?? 'Unknown date';
final size = record.servingSize ?? 0;
final unit = record.unit?.toJson() ?? 'g';
print('$consumedAt: ${record.dishName ?? "Unknown"} - $size$unit');
}
} catch (e) {
print('Failed to fetch records: $e');
}
```
### Get a Specific Record
Retrieve a single nutrition record by its ID:
```dart theme={null}
try {
final record = await spikeConnection.getNutritionRecord(id: recordId);
if (record != null) {
print('Dish: ${record.dishName ?? "Unknown"}');
print('Nutri-Score: ${record.nutriScore ?? "N/A"}');
// Access nutritional values
final calories = record.nutritionalFields?['energy_kcal'];
if (calories != null) {
print('Calories: $calories kcal');
}
// Access ingredients if included
for (final ingredient in record.ingredients ?? []) {
print('- ${ingredient.name}: ${ingredient.servingSize}${ingredient.unit.toJson()}');
}
} else {
print('Record not found');
}
} catch (e) {
print('Failed to fetch record: $e');
}
```
### Update Serving Size
Adjust the serving size of an existing record. All nutritional values are automatically recalculated proportionally:
```dart theme={null}
try {
final updatedRecord = await spikeConnection.updateNutritionRecordServingSize(
id: recordId,
servingSize: 200.0, // New serving size in grams
);
print('Updated serving size: ${updatedRecord.servingSize ?? 0}${updatedRecord.unit?.toJson() ?? "g"}');
print('Recalculated calories: ${updatedRecord.nutritionalFields?["energy_kcal"] ?? 0}');
} catch (e) {
print('Failed to update record: $e');
}
```
### Delete a Record
Permanently remove a nutrition record (success status is returned regardless record is found or not):
```dart theme={null}
try {
await spikeConnection.deleteNutritionRecord(id: recordId);
print('Record deleted successfully');
} catch (e) {
print('Failed to delete record: $e');
}
```
## Response Data
### `NutritionRecord`
The `NutritionRecord` class contains the analysis results:
```dart theme={null}
class NutritionRecord {
/// Report record ID
final String recordId;
/// Processing status
final NutritionRecordStatus status;
/// Detected dish name
final String? dishName;
/// Detected dish description
final String? dishDescription;
/// Dish name translated to target language
final String? dishNameTranslated;
/// Dish description translated to target language
final String? dishDescriptionTranslated;
/// Nutri-Score known as the 5-Colour Nutrition label (A-E)
final String? nutriScore;
/// Reason for processing failure
final String? failureReason;
/// Serving size in metric units
final num? servingSize;
/// Metric unit (g for solids, ml for liquids)
final NutritionalUnit? unit;
/// Nutritional values as key-value pairs
final Map? nutritionalFields;
/// List of detected ingredients with nutritional information
final List? ingredients;
/// Upload timestamp in UTC
final DateTime uploadedAt;
/// Update timestamp in UTC
final DateTime modifiedAt;
/// The UTC time when food was consumed
final DateTime? consumedAt;
}
```
### `NutritionRecordStatus`
```dart theme={null}
enum NutritionRecordStatus {
pending,
processing,
completed,
failed,
/// Unknown value was sent from API. SDK should be updated.
unknown
}
```
### `NutritionalUnit`
```dart theme={null}
enum NutritionalUnit {
g, // grams
mg, // milligrams
mcg, // micrograms
ml, // milliliters
kcal, // kilocalories
unknown
}
```
### `NutritionRecordIngredient`
```dart theme={null}
class NutritionRecordIngredient {
/// Ingredient name using LANGUAL standard terminology
final String name;
/// Ingredient name translated to target language
final String? nameTranslated;
/// Serving size in metric units
final num servingSize;
/// Metric unit (g for solids, ml for liquids)
final NutritionalUnit unit;
/// Nutritional values as key-value pairs
final Map? nutritionalFields;
}
```
### `NutritionalField`
Use this enum to specify which nutritional fields to include in the analysis:
```dart theme={null}
enum NutritionalField {
energyKcal, // energy_kcal
carbohydrateG, // carbohydrate_g
proteinG, // protein_g
fatTotalG, // fat_total_g
fatSaturatedG, // fat_saturated_g
fatPolyunsaturatedG, // fat_polyunsaturated_g
fatMonounsaturatedG, // fat_monounsaturated_g
fatTransG, // fat_trans_g
fiberTotalDietaryG, // fiber_total_dietary_g
sugarsTotalG, // sugars_total_g
cholesterolMg, // cholesterol_mg
sodiumMg, // sodium_mg
potassiumMg, // potassium_mg
calciumMg, // calcium_mg
ironMg, // iron_mg
magnesiumMg, // magnesium_mg
phosphorusMg, // phosphorus_mg
zincMg, // zinc_mg
vitaminARaeMcg, // vitamin_a_rae_mcg
vitaminCMg, // vitamin_c_mg
vitaminDMcg, // vitamin_d_mcg
vitaminEMg, // vitamin_e_mg
vitaminKMcg, // vitamin_k_mcg
thiaminMg, // thiamin_mg
riboflavinMg, // riboflavin_mg
niacinMg, // niacin_mg
vitaminB6Mg, // vitamin_b6_mg
folateMcg, // folate_mcg
vitaminB12Mcg // vitamin_b12_mcg
}
```
## Error Handling
All nutrition methods return `Future` objects that can throw exceptions. Always wrap calls in try-catch blocks:
```dart theme={null}
try {
final record = await spikeConnection.analyzeNutrition(
imageBase64: imageBase64,
consumedAt: DateTime.now(),
);
// Handle success
} on SpikeException catch (e) {
print('Spike error: ${e.message}');
} catch (e) {
print('Unexpected error: $e');
}
```
### Common Error Scenarios
| Error | Cause |
| -------------------- | --------------------------------------- |
| Invalid image format | Image is not JPEG, PNG, or WebP |
| Image too large | Base64-encoded image exceeds 10MB |
| Image too small | Image is smaller than 512×512 pixels |
| Unauthorized | Invalid or expired authentication token |
| Analysis timeout | AI processing took too long |
| Unidentifiable | Non-food image |
## Image Guidelines
For optimal analysis results, guide your users to capture images that:
1. **Center the food** — capture the plate contents as the main subject
2. **Fill the frame** — ensure the meal occupies most of the image
3. **Use proper lighting** — natural or bright lighting works best
4. **Avoid obstructions** — remove packaging and minimize utensils in frame
5. **Skip filters** — avoid filters that alter the food's appearance
See [Image Guidelines](/nutrition-ai/overview#image-guidelines) for complete recommendations.
## Best Practices
### 1. Request Only What You Need
Each additional field, ingredient breakdown, or optional data increases processing time. Only request what your app actually uses:
```dart theme={null}
// ❌ Don't request everything "just in case"
final config = NutritionalAnalysisConfig(
analysisMode: null,
countryCode: null,
languageCode: null,
includeNutriScore: true,
includeDishDescription: true,
includeIngredients: true,
includeNutritionFields: NutritionalField.values, // All 29 fields
);
// ✅ Request only what you need
final config = NutritionalAnalysisConfig(
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.carbohydrateG,
NutritionalField.fatTotalG,
],
);
```
### 2. Consider your actual UI requirements:
* Do you display ingredients? If not, skip `includeIngredients`.
* Do you show Nutri-Score? If not, skip `includeNutriScore`.
* Which nutritional values do you actually display? Request only those.
### 3. Choose the Right Processing Mode
* **Synchronous** (`analyzeNutrition`): Use when you need immediate results and can show a loading state
* **Asynchronous** (`submitNutritionForAnalysis`): Use for better UX when you don't need immediate results, or when processing multiple images
### 4. Handle All Status Values
When using asynchronous processing, always check the record status before accessing results:
```dart theme={null}
final record = await spikeConnection.getNutritionRecord(id: recordId);
if (record == null) {
// Handle not found
return;
}
if (record.status != NutritionRecordStatus.completed) {
if (record.status == NutritionRecordStatus.failed) {
// Handle failure
print('Failed: ${record.failureReason}');
} else {
// Still processing
print('Status: ${record.status}');
}
return;
}
// Safe to access results
print('Dish: ${record.dishName}');
```
### 5. Implement Webhook Handling
For production apps using asynchronous processing, implement [webhook handling](/nutrition-ai/async) on your backend to receive real-time notifications when analysis completes.
### 6. Create Reusable Configuration
If you're using the same settings across your app, create a shared configuration helper:
```dart theme={null}
// nutrition_config.dart
import 'package:spike_flutter_sdk/spike_flutter_sdk.dart';
class NutritionConfig {
static final standard = NutritionalAnalysisConfig(
analysisMode: NutritionRecordAnalysisMode.precise,
countryCode: null,
languageCode: null,
includeNutriScore: true,
includeDishDescription: null,
includeIngredients: true,
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.fatTotalG,
NutritionalField.carbohydrateG,
NutritionalField.fiberTotalDietaryG,
],
);
}
// Usage
final record = await spikeConnection.analyzeNutrition(
imageBase64: imageBase64,
consumedAt: DateTime.now(),
config: NutritionConfig.standard,
);
```
## Related Documentation
* [Nutrition AI Overview](/nutrition-ai/overview) — API overview and key features
* [Implementation Guide](/nutrition-ai/implementation) — Detailed API implementation patterns
* [Asynchronous Processing](/nutrition-ai/async) — Webhook configuration and handling
* [Nutritional Fields Reference](/technical-references/nutritional_fields) — Complete list of available nutritional fields
# Flutter SDK Overview
Source: https://docs.spikeapi.com/sdk-docs/flutter/overview
Overview of SDK-based integrations with health platforms including common principles, integration creation process, and data extraction methods for mobile health data management.
# Common principles
SDK-based integrations (Apple Health Kit, Android Health Connect, and Samsung Health Data) have a few common principles that separate them from all other providers.
* Data is stored only on the device (phone) therefore, requires to be actively extracted
* Permission control is very gradual (each metric reading requires approval)
* Data becomes available instantly as it's recorded
* Integration requires active management through SDKs
# Creating integrations
Creating these integrations does not follow the OAuth path. Meaning
* there will be no redirecting to the provider authorization page,
* and therefore no callback to URL after permissions are granted.
* Permissions are granted locally, on the phone itself, by triggering SDK methods.
* Permissions are also managed by default OS schemas, application users navigating settings menus, are granted for application package (reinstalling the app might require reauthorization), can be revoked or ignored when requested by OS based on their policies.
The schema below should explain the flow and explain the sequence of events, the moment integration gets created.
# Data extraction
Data stored only on the user's mobile device's local hardware (encrypted at rest). To make data available over API, first you must select and call SDK functions dedicated to sleep, workouts or other metrics reading.
The schema below should explain the flow and explain the sequence of events, the moment when data becomes available for reading over API and SDK.
# Events sequence schema
# Flutter SDK Setup
Source: https://docs.spikeapi.com/sdk-docs/flutter/setup
This document provides a setup guide of the Spike SDK for Flutter platform.
## Version
**Current Flutter SDK Version:** `4.7.14`
## Resources
* Flutter Package: [Available here](https://pub.dev/packages/spike_flutter_sdk)
* Example app: [Available here](https://gitlab.com/spike_api/public/spike-sdk-examples/-/tree/master/flutter-v3/flutter_spike_v3)
## Requirements
* **iOS Version**: `13.0+`
* **Xcode 26+** is required.
* **Android Version**: `9.0+` (Level 28, P, Pie)
## Installation
Add a dependency on Spike SDK in your pubspec.yaml file:
```yaml theme={null}
dependencies:
spike_flutter_sdk: ^4.3.34
```
## iOS Setup Guide
Use `pod install` and `pod update` commands from `ios/` folder of your app to install/update `SpikeSDK`.
### iOS Signing & Capabilities
To add HealthKit support to your application's Capabilities.
* Open the `ios/` folder of your project in Xcode
* Select the project name in the left sidebar
* Open the Signing & Capabilities section
* In the main view select '+ Capability' and double-click HealthKit
More details you can find [here](https://developer.apple.com/documentation/healthkit/setting_up_healthkit).
### Info.plist
Add Health Kit permissions descriptions to your `Info.plist` file:
```xml theme={null}
NSHealthShareUsageDescription
We will use your health information to better track workouts.
NSHealthUpdateUsageDescription
We will update your health information to better track workouts.
```
## Android Setup Guide
To add the SDK to your Android project, you have to add the following to your project's `build.gradle` file in the repositories block.
```gradle theme={null}
allprojects {
repositories {
// Other repositories
maven {
url 'https://gitlab.com/api/v4/projects/43396247/packages/maven'
}
}
}
```
Set Android SDK version in `local.properties` file:
```text theme={null}
flutter.minSdkVersion=28
flutter.compileSdkVersion=34
```
### Android Permissions
Include the necessary health permissions in your `AndroidManifest.xml` to fully leverage the Spike SDK and access data from apps integrated with Health Connect. Please refer to [this guide](https://developer.android.com/health-and-fitness/guides/health-connect/get-started#declare-permissions) for details on the required permissions.
**Note**: Only request permissions essential to your app’s functionality. Requesting unused permissions will lead to app store rejection!
```xml theme={null}
```
Add an intent filter to your activity definition so that you can request the permissions at runtime.
```xml theme={null}
```
Check the contents of `android/app/src/main/kotlin/{YOUR_PACKAGE_ID}/MainActivity.kt`. You must see something like that in case it is the new app being developed:
```kotlin theme={null}
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
```
You must change `FlutterActivity` to the `FlutterFragmentActivity` which means that your code should turn into the result similar to the one below:
```kotlin theme={null}
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity() {
}
```
In the Health Connect permissions activity, there is a link to your privacy policy. You need to grant the Health Connect app access to link back to your privacy policy. In the example below, you should either replace .MainActivity with an activity that presents the privacy policy or have the Main Activity route the user to the policy. This step may be required to pass Google app review when requesting access to sensitive permissions.
```xml theme={null}
```
**Note**: If permissions handling is not working, this might be related to launch mode being singleTop. This might be not needed, but some apps face problems when requesting permissions.
If you face them, then you should try removing the property `android:launchMode="singleTop"`.
**Note**: If the app is not building, it might be related to the label replacement issue. In this case, you should add `tools:replace="android:label"` to the application tag:
```xml theme={null}
Please note that you need to call **requestHealthPermissions** each time users restart their app after making Spike Create Connection.
## Step 3: Get Data
The maximum permitted query date range on Android Health Connect is 90 days
There are four types of data you can retrieve from Spike:
* **Statistics** are calculated values derived from records.
* **Activities** are data about a user's activities or workouts.
* **Sleep** is data about a user's sleep.
* **Records** consist of the raw data points collected from user devices or applications.
### Statistics
Now you can read hourly statistics data of steps and distance for today:
```dart theme={null}
final now = DateTime.now();
final tomorrow = DateTime(now.year, now.month, now.day + 1);
final weekAgo = now.subtract(const Duration(days: 7));
final statistics = await widget.connection.getStatistics(
ofTypes: [StatisticsType.steps],
from: weekAgo,
to: tomorrow,
interval: StatisticsInterval.day,
);
```
where:
```dart theme={null}
enum StatisticsType {
steps,
distanceTotal,
distanceWalking,
distanceCycling,
distanceRunning,
caloriesBurnedTotal,
caloriesBurnedBasal,
caloriesBurnedActive;
}
enum StatisticsInterval {
hour,
day;
}
// Filter
class StatisticsFilter {
final bool excludeManual;
final Set? providers;
final Set? activityTags;
}
// Result:
class Statistic {
final DateTime start;
final DateTime end;
final int duration;
final StatisticsType type;
final num value;
final Unit unit;
final int? rowCount;
final List? recordIds;
}
```
### Records
```dart theme={null}
final now = DateTime.now();
final tomorrow = DateTime(now.year, now.month, now.day + 1);
final weekAgo = now.subtract(const Duration(days: 7));
final records = await connection.getRecords(
ofTypes: [MetricType.stepsTotal],
from: weekAgo,
to: tomorrow,
filter: StatisticsFilter(excludeManual: false),
);
```
where:
```dart theme={null}
enum MetricType {
heartrateMax,
heartrateAvg,
heartrateMin,
heartrate,
heartrateResting,
heartrateVariabilityRmssd,
heartrateVariabilityRmssdDeepSleep,
elevationMax,
elevationAvg,
elevationMin,
elevationGain,
elevationLoss,
ascent,
descent,
caloriesBurnedActive,
caloriesBurnedBasal,
caloriesBurned,
caloriesIntake,
stepsTotal,
floorsClimbed,
distanceTotal,
distanceWalking,
distanceCycling,
distanceRunning,
distanceWheelchair,
distanceSwimming,
speedMax,
speedAvg,
speedMin,
airTemperatureMax,
airTemperatureAvg,
airTemperatureMin,
spo2Max,
spo2Avg,
spo2Min,
longitude,
latitude,
elevation,
durationActive,
swimmingLengths,
swimmingDistancePerStroke;
}
// Result:
class Record {
final String recordId;
final String? inputMethod;
final DateTime startAt;
final DateTime? endAt;
final DateTime modifiedAt;
final int? duration;
final String? provider;
final String? providerSource;
final bool? isSourceAggregated;
final RecordSource? source;
final Map? metrics;
final List? activityTags;
final String? activityType;
final List? sessions;
final List? laps;
final List? segments;
final List? splits;
final List? samples;
final List? routePoints;
final List? sleep;
}
class ActivityEntry {
final int? divisionRef;
final String? divisionLabel;
final DateTime? startAt;
final DateTime? endAt;
final int? duration;
final Map? metrics;
}
```
# Flutter SDK for Samsung Health
Source: https://docs.spikeapi.com/sdk-docs/flutter/usage-guide-shd
Start getting Spike data in 3 steps using Spike SDK for Samsung Health Data on Flutter platform.
## Requirements and Limitations
Samsung Health Data is available on Android devices only!
* Samsung Health Data SDK runs on devices with Android 10 (API level 29) or above. It is available on all Samsung smartphones and non-Samsung Android smartphones.
* Samsung Health Data SDK works with Samsung Health. Samsung Health version 6.30.2 or higher is required.
* Samsung Health Data SDK supports Java 17 or higher version.
* The emulator is not supported.
* Data obtained using Samsung Health Data SDK is for fitness and wellness information only. It is not for the diagnosis or treatment of any medical condition.
## Step 1: Create a Spike Connection
If you already set up [Google Health Connect integration](/sdk-docs/flutter/usage-guide) in your app, you should skip to [this step](#step-2%3A-ask-user-for-permissions) and use the same Spike SDK connection object.
To set up the Spike SDK create `SpikeConnectionV3` instance with your Spike application id, application user id and signature unique to
each of your application users (more on generating signatures [here](/api-docs/authentication)):
```dart theme={null}
import 'package:spike_flutter_sdk/spike_flutter_sdk.dart';
final spikeConnection = await SpikeSDKV3.createConnection(
applicationId: 1000,
signature: "signature",
endUserId: "user-id",
);
```
## Step 2: Ask User for Permissions
If you want to read data from Samsung Health, you have to ensure the user gives your app permissions.
First, you have to check if Samsung Health is available on users' phone using
`checkSamsungHealthDataAvailability` method:
```dart theme={null}
final availability = await spikeConnection.checkSamsungHealthDataAvailability()
```
where:
```dart theme={null}
class SamsungHealthAvailability {
final SamsungHealthAvailabilityStatus status;
final int errorCode;
final String message;
}
enum SamsungHealthAvailabilityStatus {
// Samsung Health is not installed. Ask user to install it.
notInstalled,
// The version of Samsung Health is too old. Ask users to update it.
updateRequired,
// The Samsung Health Data is installed but is disabled.
disabled,
// Samsung Health has been installed but the user didn't perform an initial process, such as
// agreeing to the Terms and Conditions.
notInitialized,
// Samsung Health returned other error.
errorOther,
// Samsung Health Data is available.
installed,
}
```
If Samsung Health is installed, you can ask user for permissions using `requestPermissions` method:
```dart theme={null}
// Samsung Health integration has to be enabled in Spike SDK connection before
// using further methods for reading data or managing permissions:
spikeConnection.enableSamsungHealthDataIntegration()
await spikeConnection.requestSamsungHealthDataPermissions(
statisticTypes: [StatisticsType.steps],
);
```
Please note that users might only grant partial permissions. In such cases, it's up to you to decide whether your app can function effectively with limited access.
The Spike SDK itself will still operate even without full permissions; however, it may result in no data being returned in certain scenarios.
You can now use `StatisticsFilter(providers: {Provider.samsungHealthData})` to specifically retrieve data from Samsung Health. Alternatively, you can omit the
providers parameter entirely and allow Spike to choose the most suitable data source based on your request.
## Step 3: Get Data
Reading data is the same as in Android for [Google Health Connect](/sdk-docs/android/usage-guide#step-3%3A-get-data). The only difference is that
you may want to filter results by `Provider.samsungHealthData` to get only Samsung Health data:
```dart theme={null}
StatisticsFilter(providers: {Provider.samsungHealthData})
```
## Background Delivery
Samsung Health data can be delivered in the background the same way as Apple HealthKit or Android Health Connect.
If you want to use background delivery to get Samsung Health data, you have to enable Samsung Health Data integration first:
```dart theme={null}
spikeConnection.enableSamsungHealthDataIntegration()
```
After enabling Samsung Health Data integration, you can use background delivery normally.
See the [background delivery section](/sdk-docs/flutter/background-delivery) for more details.
## Developer Mode for Testing
To test Samsung Health Data integration on your phone, you have to enable developer mode
in the Samsung Health app:
1. Tap the ‘⋮’ button of Samsung Health in the top-right.
2. Select Settings > About Samsung Health.
3. Tap the version line region quickly 10 times or more.
If you are successful, the Developer mode (Samsung Health Data SDK) button is displayed.
4. Select Developer mode (Samsung Health Data SDK) .
5. Agree with the Notice about usage of the Developer mode.
6. To read data from Samsung Health with Samsung Health Data SDK, turn Developer Mode for Data Read on.
After your app is ready for release, you should apply for a Samsung Partnership Agreement. For more information, please contact Spike support.
The Samsung Health developer mode is ONLY intended for testing or debugging your app. It is NOT for app users. Do not provide a developer mode guide to app users.
## See Also
* [Samsung error codes](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-availability/error-code.html)
* [SamsungHealthDataPermissionManager](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-permission-manager/index.html)
* [SamsungHealthDataAvailabilityStatus](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-availability-status/index.html)
* [SpikeConnectionV3.enableSamsungHealthDataIntegration](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/enable-samsung-health-data-integration.html)
* [SpikeConnectionV3.disableSamsungHealthDataIntegration](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/disable-samsung-health-data-integration.html)
* [SpikeConnectionV3.isSamsungHealthDataIntegrationEnabled](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/is-samsung-health-data-integration-enabled.html)
# iOS SDK Backfill
Source: https://docs.spikeapi.com/sdk-docs/ios/backfill
Backfill historical data from Apple HealthKit.
## Backfill from Apple HealthKit
Data is stored exclusively on the device (iPhone). Backfilling is possible from the moment the application user grants permissions and is limited by:
* What data the user has on their device
* What permissions your app has been granted (read access for specific data types)
* User's health app settings or deletions
* Your retention policy settings
## Manual Data Extraction Required
Because Apple doesn't offer any other communication except HealthKit framework, you **must query needed data manually** to backfill. Before data is extracted, it won't be available to your backend over API calls. Enabling background data delivery also won't perform the backfill as it's designed for new data events.
## Implementation
To implement backfill functionality, query historical data using the SDK's statistics methods for your desired date range:
```swift theme={null}
import SpikeSDK
// Example: Backfill last 7 days of steps data
let calendar = Calendar.current
let today = Date()
for dayOffset in 0..<7 {
guard let startDate = calendar.date(byAdding: .day, value: -dayOffset, to: today) else { continue }
guard let endDate = calendar.date(byAdding: .day, value: 1, to: startDate) else { continue }
do {
let statistics = try await spikeConnection.getStatistics(
ofTypes: [.steps],
from: startDate,
to: endDate,
interval: .day,
filter: StatisticsFilter(providers: [.apple])
)
// Process the statistics data
for statistic in statistics {
print("Steps for \(statistic.start): \(statistic.value)")
}
} catch {
// Handle specific error types for better user experience
if let hkError = error as? HKError {
switch hkError.code {
case .errorAuthorizationNotDetermined:
print("Authorization not determined. Request permissions first.")
case .errorAuthorizationDenied:
print("Access denied. Guide user to Health app settings.")
default:
print("HealthKit error: \(hkError.localizedDescription)")
}
} else {
print("Error fetching statistics for \(startDate): \(error.localizedDescription)")
}
}
}
```
## Best Practices
### Performance and User Experience
* Keep the backfill process asynchronous for the best user experience
* Segment requests into smaller date ranges to ensure optimal performance
* Implement rate limiting to avoid overwhelming the HealthKit store
* Consider implementing a progress indicator for longer backfill operations
### Privacy and Security
* Handle permissions gracefully — users may grant partial access
* Consider allowing users to control the backfill scope (date range, data types)
* Comply with relevant regulations such as GDPR for user data protection
### Error Handling
* Implement robust error handling for authorization failures
* Gracefully handle scenarios where data is unavailable or incomplete
* Manage data conflicts and duplicates effectively to maintain data integrity
* Provide meaningful feedback to users about data access issues
* Guide users to Health app settings when permissions are denied
### Data Management
* Avoid storing HealthKit data outside the HealthKit store unless necessary
* If external storage is required, ensure data is encrypted and secure
* Validate data before processing to ensure accuracy and consistency
* Respect a user's ability to revoke permissions at any time
# iOS SDK Background Delivery
Source: https://docs.spikeapi.com/sdk-docs/ios/background-delivery
Background delivery ensures that data updates are sent to your backend via webhooks, even when the application is in the background or closed.
### Important Notes About Background Delivery on iOS
* For most data types, the most possible frequency of updates is 1 hour.
* iOS can update data more frequently for some data types, for example, vo2 max.
* iOS may throttle the frequency of updates for background delivery depending on the app's activity, battery state, etc.
* Background delivery is not possible while a device is locked, so it will be executed only when the device is unlocked.
* iOS may stop background delivery if it detects that the app is not active for a long time.
* The feature is available starting with iOS 15.
**Important:** The Spike SDK, along with any other HealthKit applications, cannot guarantee data synchronization on a fixed schedule. The hourly sync
interval serves as a guideline rather than a strict requirement enforced by iOS. Consequently, the actual synchronization frequency may vary,
occurring hourly, once per day, or during specific system-defined events, such as the conclusion of Sleep Mode or when the device begins charging.
## Setup
### Enable Background Delivery for the Application Target
* Open the folder of your project in Xcode
* Select the project name in the left sidebar
* Open the Signing & Capabilities section
* Select HealthKit background delivery under the HealthKit section
### Initialization at Application Startup
Add Spike initialization code to your AppDelegate inside application:didFinishLaunchingWithOptions: method:
```swift theme={null}
import SpikeSDK
...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
Spike.configure()
...
}
```
For SwiftUI-based apps follow few steps:
Create a custom class that inherits from NSObject and conforms to the UIApplicationDelegate protocol:
```swift theme={null}
import SpikeSDK
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
Spike.configure()
return true
}
}
```
And now in your App struct, use the UIApplicationDelegateAdaptor property wrapper to tell SwiftUI it should use your AppDelegate class for the application delegate:
```swift theme={null}
.@main
struct YourApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
...
}
```
### Ask Spike SDK for Background Delivery
To enable background delivery, you need to call the enableBackgroundDelivery method:
```swift theme={null}
do {
try await spikeConnection.enableBackgroundDelivery(
forStatisticsTypes: [.steps]
)
} catch {
print("\(error)")
}
```
Calling enableBackgroundDelivery will overwrite all the previous settings, so you have to call it with all the data types that you want in one call. It accepts all the possible data types that can be delivered in the background (statistics, records, activities and sleep).
```swift theme={null}
func enableBackgroundDelivery(
forStatistics: [StatisticsType]?,
forMetrics: [MetricType]?,
forActivities: [ActivityConfig]?,
forSleep: [SleepConfig]?
) async throws
or
public struct BackgroundDeliveryConfig: Codable, Hashable, Sendable {
public var statisticsTypes: [StatisticsType]?
public var metricsTypes: [MetricType]?
public var activityConfigs: [ActivityConfig]?
public var sleepConfigs: [SleepConfig]?
}
```
You can also call `getBackgroundDeliveryConfig()` to get the current configuration and `disableBackgroundDelivery()` to disable background delivery.
# iOS SDK Changelog
Source: https://docs.spikeapi.com/sdk-docs/ios/changelog
A changelog documenting version updates and changes of Spike SDK for iOS platform.
# Spike iOS SDK Changelog
## 4.7.11
* Removed the deprecated v1/v2 connection API
## 4.6.11
* Added HealthKit-backed nutrition support to the v3 HealthKit flow. You can now request dietary permissions, backfill historical nutrition data, and include nutrition fields in background delivery by passing `forNutrition` / `nutritionalFields`.
```
try await spikeConnection.requestPermissionsFromHealthKit(
forNutrition: [.energyKcal, .proteinG, .carbohydrateG, .fatTotalG]
)
try await spikeConnection.backfill(
forNutrition: [.energyKcal, .proteinG, .carbohydrateG, .fatTotalG],
days: 7
)
```
* `getNutritionRecords(from:to:)` now reads requested HealthKit nutrition data before returning nutrition records when HealthKit integration is enabled.
## 4.5.21
* Added `backfill` on the v3 connection to upload historical HealthKit data for a chosen number of days. Call it after `requestPermissionsFromHealthKit(forStatistics:forMetrics:forActivities:forSleep:)` and pass the statistics, metrics, activities, and sleep configurations you want to fill in; only the categories you provide are pushed.
```
try await spikeConnection.backfill(
forStatistics: [.steps],
forMetrics: nil,
forActivities: nil,
forSleep: nil
)
```
* Added `requestPermissionsFromHealthKitAndBackfill` — a convenience method that requests HealthKit permissions and immediately starts backfilling historical data. Today and yesterday are pushed before the method returns so you can display data right away; older days continue uploading in the background.
```
try await spikeConnection.requestPermissionsFromHealthKitAndBackfill(
forStatistics: [.steps],
forMetrics: nil,
forActivities: nil,
forSleep: nil,
backfillDays: 7
)
```
* Fixed concurrency issue
## 4.5.11
* Improved handling of deleted integrations — background delivery is now automatically disabled when the integration no longer exists
* Improved error handling for API responses
* Added `RecordConfig` for configuring records queries:
* `includeSamples` - whether to include samples (raw data points) in the response
* Updated `getRecords` to accept an optional `RecordConfig` parameter:
```
spikeConnection.getRecords(
ofTypes: metricTypes,
from: startDate,
to: endDate,
filter: StatisticsFilter(excludeManual: true),
configured: RecordConfig(includeSamples: true)
)
```
## 4.4.11
* Updated `Unit` enum with new values:
* Added: `miles`, `mPerSec`, `fahrenheit`, `ms`, `seconds`, `degrees`, `sleepStage`, `secPerM`, `rpm`, `spm`, `breathsPerMin`, `g`, `lbs`, `st`, `mmHg`, `mLPerKgPerMin`, `uV`, `mgPerDl`, `w`
* Removed: `kmh`, `kmPerMin`
* Improved data communication with API
* Enhanced the `getSleep` endpoint.
Important:
* Dates provided are now interpreted as dates only; the time component is ignored when querying the API.
* The primary night sleep period is associated with the calendar date on which it ends.
## 4.3.161
* Better Nutrition Facts Label Recognition response parsing
## 4.3.151
* Improved data push to API
* Fixed workout digest generation for caching
* Improved client cache configuration
## 4.3.141
* Added Nutrition Facts Label Recognition for extracting nutritional information from label photos.
For more information please visit our [documentation](https://docs.spikeapi.com).
* `recognizeNutritionFactsLabel(imageBase64:config:)` - Analyze a nutrition facts label image and extract nutritional data
* `recognizeNutritionFactsLabel(image:config:)` - Convenience method that accepts UIImage
* Added methods for manual nutrition record management:
* `createNutritionRecord(_:)` - Insert a new nutrition record with custom data
* `replaceNutritionRecord(_:)` - Replace an existing nutrition record completely
* Added `NutritionRecord` extension for creating and manipulating nutrition records:
* `NutritionRecord.init(...)` - Create a new nutrition record with specified properties
* `NutritionRecord.add(ingredient:)` - Add an ingredient to a record
* `NutritionRecord.remove(ingredient:)` - Remove an ingredient from a record
* `NutritionRecord.update(servingSize:unit:)` - Update serving size and unit
* Added `NutritionRecordIngredient` extension for creating and manipulating ingredients:
* `NutritionRecordIngredient.init(...)` - Create a new nutrition record ingredient
* `NutritionRecordIngredient.set(nutritionalField:value:)` - Set a nutritional field value
* `NutritionRecordIngredient.remove(nutritionalField:)` - Remove a nutritional field
* `NutritionRecordIngredient.update(servingSize:)` - Scale serving size and nutritional values proportionally
* Nutrition AI methods now return `NutritionRecordAnalysisResult` instead of `NutritionRecord`:
* `NutritionRecordAnalysisResult` wraps the nutrition record with additional metadata including `status`, `recordId`, `uploadedAt`, and `failureReason`
* Access the nutrition data via the `nutritionRecord` property
* Affected methods: `analyzeNutrition`, `getNutritionRecords`, `getNutritionRecord`, `updateNutritionRecordServingSize`, `createNutritionRecord`, `replaceNutritionRecord`
* Added new `NutritionRecordStatus` case: `.updated`
## 4.3.131
* Added Nutrition AI features for analyzing food images. For more information
please visit our [documentation](https://docs.spikeapi.com).
* `analyzeNutrition(imageBase64:consumedAt:config:)` - Analyze food image synchronously and wait for results
* `analyzeNutrition(image:consumedAt:config:)` - Convenience method that accepts UIImage
* `submitNutritionForAnalysis(imageBase64:consumedAt:config:)` - Analyze food image asynchronously, returns record ID
* `submitNutritionForAnalysis(image:consumedAt:config:)` - Convenience method that accepts UIImage
* `getNutritionRecords(from:to:)` - Retrieve nutrition records for a date range
* `getNutritionRecord(id:)` - Get a specific nutrition record by ID
* `deleteNutritionRecord(id:)` - Delete a nutrition record
* `updateNutritionRecordServingSize(id:servingSize:)` - Update serving size for a nutrition record
* Added new `Provider`:
* luna
* Added new `ProviderSource` types:
* fitbitSkinTemperatureSummary
* garminWellnessSkinTemperatureSummary
* lunaSleep
* Removed `ProviderSource` types:
* fitbitUserActivitiesList
* fitbitUserActivitiesDate
* fitbitUserActivitiesHeartDate
* fitbitUserActivitiesHeartDateInterday
* withingsMeasureGetWorkouts
* withingsMeasureGetActivity
* withingsMeasureGetMeas
* Removed `ActivityTag`:
* onBicycle
* Removed `Provider`:
* wahoo
## 4.3.121
* Implemented enabling HealthKit integration in the admin console right after requesting permissions
## 4.3.111
* Removed `providerUserIdentifier` from `IntegrationInitConfig`
* Added ability to read mindfulness activities:
```
spikeConnection.getActivities(
configured: ActivityConfig(activityCategories: [.mindfulness])
from: dateFrom,
to: dateTo
)
```
* Added new `ActivityTag` types:
* mindfulness
* Added new `ActivityType` types:
* ecgMeasurement
* triathlon
* biathlon
* duathlon
* rollerblading
* skateboarding
* skating
* calisthenics
* weightLifting
* canoeing
* floorball
* jiuJitsu
* diving
* orienteering
* bootcamp
* motorsports
* horseriding
* paragliding
* multisport
* bloodTest
* mindfulnessSession
* Added new `ProviderSource` types:
* appleHealthkitMindfulness
* healthConnectMindfulnessSession
* Added new `ActivityCategory` type:
* mindfulness
* Added new `MetricType`:
* heartrateRestingMin
* heartrateRestingMax
* cadence
* cadenceMin
* cadenceMax
* pace
* airTemperature
* bodyTemperatureMax
* bodyTemperatureMin
* basalBodyTemperature
* basalBodyTemperatureMax
* basalBodyTemperatureMin
* skinTemperatureMax
* skinTemperatureMin
* sleepSkinTemperatureDeviation
* Added new `StatisticsType`:
* sleepSkinTemperatureDeviation
* hrvRmssd
* hrvSdnn
* Fixed typo in metric type: `swimmingLengths` (was incorrectly spelled as `swimming_lenghts`)
## 4.3.101
* Improved how sleep data is read for sleep score
## 4.3.91
* Added `.coros` `Provider`
* Updated `ProviderSource` enum
* Improved the way data is sent to API to reduce the size and speed of calls to Spike
* Added new Statistic types:
* stressScore
* recoveryScore
* activityScore
* Added new Metric types for `getSleep` request:
* sleepDuration
* sleepDurationAwake
* sleepDurationDeep
* sleepDurationLight
* sleepDurationNap
* sleepDurationRem
* sleepEfficiency
* sleepInterruptions
* sleepLatency
* sleepScore
* Better keychain compatibility
NOTE: If you use the keychain in your app with a service name equal to your bundle
identifier, please check if it contains key named `spikeApiToken` and delete it.
## 4.3.81
* Added `MetricType`: `.glucose`
## 4.3.71
* The old `SpikeSDK` API has been deprecated
* Added `disableHealthKitIntegration` and `isHealthKitIntegrationEnabled` functions in spike connection
## 4.3.61
* Added .distanceSwimming into the list of metrics available in activities
* Added new providers: .dexcom, .freestyleLibre, .huawei, .strava
## 4.3.51
* Added new `Provider`:
* samsungHealthData
* Added new `ProviderSource`:
* samsungHealthDataAggregation
* Renamed `MetricTypes`:
* `sleepBreathingRate` to `breathingRate`
* `sleepBreathingRateMin` to `breathingRateMin`
* `sleepBreathingRateMax` to `breathingRateMax`
## 4.3.41
* IntegrationInitConfig is now codable
* Additional check in `getIntegrationInitUrl` for email in ultrahuman integration
## 4.3.31
* Added new metric types:
* bodyFat
* bodyFatMax
* bodyFatMin
* bodyBoneMass
* bodyMassIndex
* bloodPressureSystolic
* bloodPressureSystolicMax
* bloodPressureSystolicMin
* bloodPressureDiastolic
* bloodPressureDiastolicMax
* bloodPressureDiastolicMin
* Added new fields in `UserProperties`:
* bodyBoneMass
* bodyFat
* bodyMassIndex
* Updated `getIntegrationInitUrl`. Now it accepts `IntegrationInitConfig` config object where you can pass:
* `redirectUri`: will override the one set in admin console
* `state`: when the authorization server redirects back to the client, it includes the `state` value originally sent
* `providerUserIdentifier`: at the moment used (and required) only when integrating with ultrahuman
* New provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider: .ultrahuman, config: IntegrationInitConfig(providerUserIdentifier: "user@mail.com"))
```
* Improved transport protocol for background delivery
## 4.3.21
* Improved how statistics are read from HealthKit
## 4.3.11
* Improved transport protocol for even faster requests to Spike API
* Added new statistic metrics: heartrate, heartrateMax, heartrateMin, heartrateResting
## 4.2.31
* Added sleepScore property in the Record type
* Added new metrics:
* spo2
* bodyTemperature
* skinTemperature (available only on getSleep())
* sleepBreathingRate (available only on getSleep())
* sleepBreathingRateMin (available only on getSleep())
* sleepBreathingRateMax (available only on getSleep())
* sleepBreathingRateAvg (available only on getSleep())
## 4.2.21
* New `getUserProperties` method for reading: weight, height, timezone, birthdate, gender
* New metric: VO2max (Cardio Fitness)
* New statistic: sleep score
## 4.2.11
* 🔴 **Breaking Change**: Update to `Spike.createConnectionAPIv3`. The method
`Spike.createConnectionAPIv3(appId:, authToken:, customerEndUserId:)` has been updated to:
**`Spike.createConnectionAPIv3(applicationId:, signature:, endUserId:)`**
* The `applicationId` parameter must now be provided as an **`Int`**.
* The `signature` parameter now requires an **`HMAC-SHA256` signed user ID**.
* ⚠️ **Security Notice:**
* **Do not store your HMAC signing key within the application itself**, as this poses a security risk.
* Instead, generate and provide the signature from your backend.
* **Legacy Support**: For **development purposes only**, the previous connection flow remains available under the renamed method:
`Spike.createConnectionAPIv3_legacy(appId:, authToken:, customerEndUserId:)`
## 4.1.11
* Added background delivery
* Added log callback
* Added more sleep data to be sent
## 4.0.11
Completely new SDK! Please see our official documentation for more details and usage instructions.
## 2.4.5
* Fixed the cache issue in the release process
## 2.4.4
* Reverted version 2.4.3
* Implemented reading more distance fields in a workout object from HealthKit
## 2.4.3
* Added third party integration initialization.
* Implemented reading more distance fields in a workout object from HealthKit
## 2.4.2
* Added customer user id validation.
## 2.4.1
* Fixed the timezone in case it changes while the app is running
## 2.4.0
* Implemented a better way to authenticate with API
* Added ECG
## 2.3.2
* Added trigger property to `extractAndPostData` request
## 2.3.1
* Added cycling parameters to Activities stream data request
## 2.3.0
* Removed environment configuration
## 2.2.3
* Added prop `provider_timestamp` to an Activities summary model
## 2.2.2
* Included intraday entries of a Steps intraday data type for range requests
## 2.2.1
* Added handling Steps an intraday entries metadata object
## 2.2.0
* Aligned SpikeData wrapper model with the server
## 2.1.14
* Fixed date range parsing
## 2.1.13
* Step intraday data improvements
## 2.1.12
* Step intraday data improvements
## 2.1.11
* Expose isHealthDataAvailable method
## 2.1.10
* Use iso8601 standardized Calendar
# iOS SDK Logging
Source: https://docs.spikeapi.com/sdk-docs/ios/logging
Getting logs for troubleshooting and debugging from Spike SDK for iOS platform.
## Code Examples
To receive logs from Spike SDK, use the following code:
```swift theme={null}
Spike.setLogCallback { level, message in
print("\(message)")
}
```
If you are using background delivery, ensure this code runs in your application's `didFinishLaunchingWithOptions` method before you start using Spike SDK:
```swift theme={null}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
Spike.setLogCallback { level, message in
print("\(message)")
}
Spike.configure()
...
}
```
# iOS SDK Nutrition AI
Source: https://docs.spikeapi.com/sdk-docs/ios/nutrition-ai
Analyze food images and retrieve nutritional information using AI-powered analysis in your iOS app.
## About
The Spike SDK provides a convenient interface for the [Nutrition AI API](/nutrition-ai/overview), allowing you to analyze food images directly from your iOS application. The SDK handles image encoding, API communication, and response parsing, making it easy to integrate nutritional analysis into your app.
All Spike SDK async method calls should be wrapped in a `do-catch` block. See [Error Handling](#error-handling) for details.
## Key Features
* **AI-Powered Analysis** — advanced computer vision for food identification and nutritional calculations
* **Flexible Processing** — choose between synchronous (wait for results) or asynchronous (background) processing
* **`UIImage` Support** — convenient methods that accept `UIImage` directly, in addition to base64-encoded strings
* **Complete Record Management** — retrieve, update, and delete nutrition records
## Available Methods
| Method | Description |
| ------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
| `analyzeNutrition(image:consumedAt:config:)` | Submit food image for synchronous processing and wait for the analysis results |
| `submitNutritionForAnalysis(image:consumedAt:config:)` | Submit food image for asynchronous processing and get record ID immediately for polling afterwards |
| `getNutritionRecords(from:to:)` | Retrieve nutrition records for a datetime range |
| `getNutritionRecord(id:)` | Get a specific nutrition record by ID |
| `updateNutritionRecordServingSize(id:servingSize:)` | Update serving size for a nutrition record |
| `deleteNutritionRecord(id:)` | Delete a nutrition record by ID |
## Analyzing Food Images
### Synchronous Processing
Use synchronous analysis when you want to wait for the complete nutritional analysis before proceeding. This is ideal for scenarios where you need immediate results and can display a loading indicator.
```swift theme={null}
import SpikeSDK
let image: UIImage = // ... captured from camera or photo library
do {
let record = try await spikeConnection.analyzeNutrition(
image: image,
consumedAt: Date(),
config: NutritionalAnalysisConfig(
analysisMode: .precise,
includeIngredients: true,
includeNutriScore: true
)
)
print("Dish: \(record.dishName ?? "Unknown")")
print("Serving size: \(record.servingSize ?? 0) \(record.unit?.rawValue ?? "g")")
print("Calories: \(record.nutritionalFields?["energy_kcal"] ?? 0)")
} catch {
print("Analysis failed: \(error)")
}
```
You can also use base64-encoded image data:
```swift theme={null}
// Using base64-encoded string
let imageData = image.jpegData(compressionQuality: 0.8)!
let base64String = imageData.base64EncodedString()
let record = try await spikeConnection.analyzeNutrition(
imageBase64: base64String,
consumedAt: Date(),
config: nil // Uses default configuration
)
```
**Processing Time**: Synchronous processing takes some time depending on image complexity. Consider showing a loading indicator to users. If you see that the analysis is taking too long, the recommendation is to use asynchronous processing instead.
### Asynchronous Processing
Use asynchronous processing when you want an immediate response without waiting for the analysis to complete. Record ID is returned.
The image is processed in the background, and you can retrieve results later by requesting nutrition analysis using the record ID or receive them via webhook.
```swift theme={null}
do {
// Submit image for background processing
let recordId = try await spikeConnection.submitNutritionForAnalysis(
image: image,
consumedAt: Date(),
config: NutritionalAnalysisConfig(
analysisMode: .fast,
includeIngredients: true
)
)
print("Analysis started. Record ID: \(recordId)")
// Optionally, poll for results later
// Your backend will also receive a webhook when analysis completes
} catch {
print("Failed to submit: \(error)")
}
```
#### Retrieving Results Asynchronously
After submitting an image for asynchronous processing, you can retrieve the results using the record ID. Check the processing status for completion success.
```swift theme={null}
// Check the status and get results
if let record = try await spikeConnection.getNutritionRecord(id: recordId) {
switch record.status {
case .completed:
print("Analysis complete: \(record.dishName ?? "Unknown")")
case .processing:
print("Still processing...")
case .pending:
print("Queued for processing...")
case .failed:
print("Analysis failed: \(record.failureReason ?? "Unknown error")")
case .unknown:
print("Unknown status. Please update SDK.")
}
}
```
For real-time notifications, configure webhooks in your [admin console](https://admin.spikeapi.com/). Your backend will receive a webhook notification when the analysis completes. See [Asynchronous Processing](/nutrition-ai/async) for webhook implementation details.
## Configuration Options
Customize the analysis using `NutritionalAnalysisConfig`:
```swift theme={null}
let config = NutritionalAnalysisConfig(
// Analysis speed vs. precision
analysisMode: .precise, // .precise (default) or .fast
// Country ISO 3166-1 alpha-2 code in lowercase
countryCode: "us",
// Language ISO 639-1 code in lowercase
languageCode: "en",
// Include Nutri-Score rating (A-E)
includeNutriScore: true,
// Include dish description
includeDishDescription: true,
// Include detailed breakdown of ingredients
includeIngredients: true,
// Specify which nutritional fields to include (using NutritionalField enum)
includeNutritionFields: [
.energyKcal,
.proteinG,
.fatTotalG,
.carbohydrateG,
.fiberTotalDietaryG,
.sodiumMg
]
)
```
### `NutritionalAnalysisConfig`
```swift theme={null}
public struct NutritionalAnalysisConfig: Codable, Hashable, Sendable {
/// A preferred mode for the analysis. Default is ".precise".
public var analysisMode: NutritionRecordAnalysisMode?
/// Country ISO 3166-1 alpha-2 code in lowercase
public var countryCode: String?
/// Language ISO 639-1 code in lowercase
public var languageCode: String?
/// Include nutri-score label of the food. Default is false.
public var includeNutriScore: Bool?
/// Include dish description of the food. Default is false.
public var includeDishDescription: Bool?
/// Include ingredients of the food. Default is false.
public var includeIngredients: Bool?
/// Include specific nutrition fields in the analysis report.
/// By default, carbohydrate_g, energy_kcal, fat_total_g and protein_g will be included.
public var includeNutritionFields: [NutritionalField]?
}
```
### Analysis Modes
```swift theme={null}
public enum NutritionRecordAnalysisMode: String, Codable, Hashable, Sendable, CaseIterable {
case fast
case precise
}
```
| Mode | Description |
| ---------- | ---------------------------------------------------------------------------- |
| `.precise` | Uses advanced AI models for highest accuracy and detailed analysis (default) |
| `.fast` | Uses optimized models for quicker processing with good accuracy |
### Default Nutritional Fields
If `includeNutritionFields` is not specified, only these basic fields are included:
* `.energyKcal`
* `.proteinG`
* `.fatTotalG`
* `.carbohydrateG`
See [Nutritional Fields Reference](/technical-references/nutritional_fields) for all available fields or check the [API Reference](https://spike_api.gitlab.io/spike-ios-sdk/documentation/spikesdk/nutritionalfield) for Swift enum values.
## Managing Nutrition Records
### List Records by Date Range
Retrieve all nutrition records within a specified date range:
```swift theme={null}
let startDate = Calendar.current.date(byAdding: .day, value: -7, to: Date())!
let endDate = Date()
do {
let records = try await spikeConnection.getNutritionRecords(
from: startDate,
to: endDate
)
for record in records {
let consumedAt = record.consumedAt?.description ?? "Unknown date"
let size = record.servingSize ?? 0
let unit = record.unit?.rawValue ?? "g"
print("\(consumedAt): \(record.dishName ?? "Unknown") - \(size)\(unit)")
}
} catch {
print("Failed to fetch records: \(error)")
}
```
### Get a Specific Record
Retrieve a single nutrition record by its ID:
```swift theme={null}
do {
if let record = try await spikeConnection.getNutritionRecord(id: recordId) {
print("Dish: \(record.dishName ?? "Unknown")")
print("Nutri-Score: \(record.nutriScore ?? "N/A")")
// Access nutritional values
if let calories = record.nutritionalFields?["energy_kcal"] {
print("Calories: \(calories) kcal")
}
// Access ingredients if included
for ingredient in record.ingredients ?? [] {
print("- \(ingredient.name): \(ingredient.servingSize)\(ingredient.unit.rawValue)")
}
} else {
print("Record not found")
}
} catch {
print("Failed to fetch record: \(error)")
}
```
### Update Serving Size
Adjust the serving size of an existing record. All nutritional values are automatically recalculated proportionally:
```swift theme={null}
do {
let updatedRecord = try await spikeConnection.updateNutritionRecordServingSize(
id: recordId,
servingSize: 200.0 // New serving size in grams
)
print("Updated serving size: \(updatedRecord.servingSize ?? 0)\(updatedRecord.unit?.rawValue ?? "g")")
print("Recalculated calories: \(updatedRecord.nutritionalFields?["energy_kcal"] ?? 0)")
} catch {
print("Failed to update record: \(error)")
}
```
### Delete a Record
Permanently remove a nutrition record (success status is returned regardless record is found or not):
```swift theme={null}
do {
try await spikeConnection.deleteNutritionRecord(id: recordId)
print("Record deleted successfully")
} catch {
print("Failed to delete record: \(error)")
}
```
## Response Data
### `NutritionRecord`
The `NutritionRecord` structure contains the analysis results:
```swift theme={null}
public struct NutritionRecord: Codable, Hashable, Sendable {
/// Report record ID
public var recordId: UUID
/// Processing status
public var status: NutritionRecordStatus
/// Detected dish name
public var dishName: String?
/// Detected dish description
public var dishDescription: String?
/// Dish name translated to target language
public var dishNameTranslated: String?
/// Dish description translated to target language
public var dishDescriptionTranslated: String?
/// Nutri-Score known as the 5-Colour Nutrition label (A-E)
public var nutriScore: String?
/// Reason for processing failure
public var failureReason: String?
/// Serving size in metric units
public var servingSize: Double?
/// Metric unit (g for solids, ml for liquids)
public var unit: NutritionalUnit?
public var nutritionalFields: [String: Double]?
/// List of detected ingredients with nutritional information
public var ingredients: [NutritionRecordIngredient]?
/// Upload timestamp in UTC
public var uploadedAt: Date
/// Update timestamp in UTC
public var modifiedAt: Date
/// The UTC time when food was consumed
public var consumedAt: Date?
}
```
### `NutritionRecordStatus`
```swift theme={null}
public enum NutritionRecordStatus: String, Codable, Hashable, Sendable, CaseIterable {
case pending
case processing
case completed
case failed
/// Unknown value was sent from API. SDK should be updated to use the newest API responses.
case unknown = "_unknown"
}
```
### `NutritionalUnit`
```swift theme={null}
public enum NutritionalUnit: String, Codable, Hashable, Sendable, CaseIterable {
case g // grams
case mg // milligrams
case mcg // micrograms
case ml // milliliters
case kcal // kilocalories
/// Unknown value was sent from API. SDK should be updated to use the newest API responses.
case unknown = "_unknown"
}
```
### `NutritionRecordIngredient`
```swift theme={null}
public struct NutritionRecordIngredient: Codable, Hashable, Sendable {
/// Ingredient name using LANGUAL standard terminology
public var name: String
/// Ingredient name translated to target language
public var nameTranslated: String?
/// Serving size in metric units
public var servingSize: Double
/// Metric unit (g for solids, ml for liquids)
public var unit: NutritionalUnit
public var nutritionalFields: [String: Double]?
}
```
### `NutritionalField`
Use this enum to specify which nutritional fields to include in the analysis:
```swift theme={null}
public enum NutritionalField: String, Codable, Hashable, Sendable, CaseIterable {
case energyKcal = "energy_kcal"
case carbohydrateG = "carbohydrate_g"
case proteinG = "protein_g"
case fatTotalG = "fat_total_g"
case fatSaturatedG = "fat_saturated_g"
case fatPolyunsaturatedG = "fat_polyunsaturated_g"
case fatMonounsaturatedG = "fat_monounsaturated_g"
case fatTransG = "fat_trans_g"
case fiberTotalDietaryG = "fiber_total_dietary_g"
case sugarsTotalG = "sugars_total_g"
case cholesterolMg = "cholesterol_mg"
case sodiumMg = "sodium_mg"
case potassiumMg = "potassium_mg"
case calciumMg = "calcium_mg"
case ironMg = "iron_mg"
case magnesiumMg = "magnesium_mg"
case phosphorusMg = "phosphorus_mg"
case zincMg = "zinc_mg"
case vitaminARaeMcg = "vitamin_a_rae_mcg"
case vitaminCMg = "vitamin_c_mg"
case vitaminDMcg = "vitamin_d_mcg"
case vitaminEMg = "vitamin_e_mg"
case vitaminKMcg = "vitamin_k_mcg"
case thiaminMg = "thiamin_mg"
case riboflavinMg = "riboflavin_mg"
case niacinMg = "niacin_mg"
case vitaminB6Mg = "vitamin_b6_mg"
case folateMcg = "folate_mcg"
case vitaminB12Mcg = "vitamin_b12_mcg"
}
```
## Error Handling
All nutrition methods can throw errors. Always wrap calls in `do-catch` blocks:
```swift theme={null}
do {
let record = try await spikeConnection.analyzeNutrition(
image: image,
consumedAt: Date(),
config: nil
)
// Handle success
} catch let error as SpikeError {
switch error {
case .invalidImage:
print("Invalid image format or size")
case .networkError(let underlying):
print("Network error: \(underlying)")
case .serverError(let message):
print("Server error: \(message)")
case .unauthorized:
print("Authentication failed")
default:
print("Error: \(error)")
}
} catch {
print("Unexpected error: \(error)")
}
```
### Common Error Scenarios
| Error | Cause |
| -------------------- | --------------------------------------- |
| Invalid image format | Image is not JPEG, PNG, or WebP |
| Image too large | Base64-encoded image exceeds 10MB |
| Image too small | Image is smaller than 512×512 pixels |
| Unauthorized | Invalid or expired authentication token |
| Analysis timeout | AI processing took too long |
| Unidentifiable | Non-food image |
## Image Guidelines
For optimal analysis results, guide your users to capture images that:
1. **Center the food** — capture the plate contents as the main subject
2. **Fill the frame** — ensure the meal occupies most of the image
3. **Use proper lighting** — natural or bright lighting works best
4. **Avoid obstructions** — remove packaging and minimize utensils in frame
5. **Skip filters** — avoid filters that alter the food's appearance
See [Image Guidelines](/nutrition-ai/overview#image-guidelines) for complete recommendations.
## Best Practices
### 1. Request Only What You Need
Each additional field, ingredient breakdown, or optional data increases processing time. Only request what your app actually uses:
```swift theme={null}
// ❌ Don't request everything "just in case"
let config = NutritionalAnalysisConfig(
includeIngredients: true,
includeNutriScore: true,
includeDishDescription: true,
includeNutritionFields: NutritionalField.allCases // All 29 fields
)
// ✅ Request only what you need
let config = NutritionalAnalysisConfig(
includeNutritionFields: [.energyKcal, .proteinG, .carbohydrateG, .fatTotalG]
)
```
### 2. Consider your actual UI requirements:
* Do you display ingredients? If not, skip `includeIngredients`.
* Do you show Nutri-Score? If not, skip `includeNutriScore`.
* Which nutritional values do you actually display? Request only those.
### 3. Choose the Right Processing Mode
* **Synchronous** (`analyzeNutrition`): Use when you need immediate results and can show a loading state
* **Asynchronous** (`submitNutritionForAnalysis`): Use for better UX when you don't need immediate results, or when processing multiple images
### 4. Handle All Status Values
When using asynchronous processing, always check the record status before accessing results:
```swift theme={null}
guard record.status == .completed else {
if record.status == .failed {
// Handle failure
} else {
// Still processing
}
return
}
// Safe to access results
```
### 5. Implement Webhook Handling
For production apps using asynchronous processing, implement [webhook handling](/nutrition-ai/async) on your backend to receive real-time notifications when analysis completes.
### 6. Cache Configuration
Create a shared configuration object if you're using the same settings across your app:
```swift theme={null}
extension NutritionalAnalysisConfig {
static let standard = NutritionalAnalysisConfig(
analysisMode: .precise,
includeIngredients: true,
includeNutriScore: true,
includeNutritionFields: [
.energyKcal, .proteinG, .fatTotalG,
.carbohydrateG, .fiberTotalDietaryG
]
)
}
// Usage
let record = try await spikeConnection.analyzeNutrition(
image: image,
consumedAt: Date(),
config: .standard
)
```
## Related Documentation
* [Nutrition AI Overview](/nutrition-ai/overview) — API overview and key features
* [Implementation Guide](/nutrition-ai/implementation) — Detailed API implementation patterns
* [Asynchronous Processing](/nutrition-ai/async) — Webhook configuration and handling
* [Nutritional Fields Reference](/technical-references/nutritional_fields) — Complete list of available nutritional fields
# iOS SDK Overview
Source: https://docs.spikeapi.com/sdk-docs/ios/overview
Overview of SDK-based integrations with health platforms including common principles, integration creation process, and data extraction methods for mobile health data management.
# Common principles
SDK-based integrations (Apple Health Kit, Android Health Connect, and Samsung Health Data) have a few common principles that separate them from all other providers.
* Data is stored only on the device (phone) therefore, requires to be actively extracted
* Permission control is very gradual (each metric reading requires approval)
* Data becomes available instantly as it's recorded
* Integration requires active management through SDKs
# Creating integrations
Creating these integrations does not follow the OAuth path. Meaning
* there will be no redirecting to the provider authorization page,
* and therefore no callback to URL after permissions are granted.
* Permissions are granted locally, on the phone itself, by triggering SDK methods.
* Permissions are also managed by default OS schemas, application users navigating settings menus, are granted for application package (reinstalling the app might require reauthorization), can be revoked or ignored when requested by OS based on their policies.
The schema below should explain the flow and explain the sequence of events, the moment integration gets created.
# Data extraction
Data stored only on the user's mobile device's local hardware (encrypted at rest). To make data available over API, first you must select and call SDK functions dedicated to sleep, workouts or other metrics reading.
The schema below should explain the flow and explain the sequence of events, the moment when data becomes available for reading over API and SDK.
# Events sequence schema
# iOS SDK Setup
Source: https://docs.spikeapi.com/sdk-docs/ios/setup
This document provides a setup guide of the Spike SDK for iOS platform.
## Version
**Current Swift SDK Version:** `4.7.11`
## Resources
* Swift Package: [Available here](https://gitlab.com/spike_api/spike-ios-sdk)
* API Reference for `SpikeSDK`: [Available here](https://spike_api.gitlab.io/spike-ios-sdk/documentation/spikesdk/spikeconnectionapiv3/)
* Example app: [Available here](https://gitlab.com/spike_api/public/spike-sdk-examples/-/tree/master/swift-v3)
## Requirements
* **iOS Version**: `13.0+`
* **Xcode 26+** is required.
## iOS Signing & Capabilities
To add HealthKit support to your application's Capabilities:
1. Open the `iOS/` folder of your project in Xcode.
2. Select the project name in the left sidebar.
3. Open the **Signing & Capabilities** section.
4. In the main view, select **+ Capability** and double-click **HealthKit**.
For more details, see [Apple's HealthKit setup guide](https://developer.apple.com/documentation/healthkit/setting_up_healthkit).
## Info.plist
Add Health Kit permissions descriptions to your `Info.plist` file:
```xml theme={null}
NSHealthShareUsageDescription
We will use your health information to better track workouts.
NSHealthUpdateUsageDescription
We will update your health information to better track workouts.
```
## SDK Installation
### CocoaPods
CocoaPods is a dependency manager for Cocoa projects. To integrate `SpikeSDK` into your Xcode project using CocoaPods, specify it in your `Podfile`:
```
pod 'SpikeSDK'
```
Use `pod install` and `pod update` commands to install/update pods afterward.
### Swift Package Manager
To integrate `SpikeSDK` into your Xcode project using Swift Package Manager, add it in your `Package.swift` or through the Project's **Package Dependencies** tab:
```swift theme={null}
dependencies: [
.package(url: "https://gitlab.com/spike_api/spike-ios-sdk", .upToNextMinor(from: "4.7.11"))
]
```
# iOS SDK Usage Guide
Source: https://docs.spikeapi.com/sdk-docs/ios/usage-guide
Start getting Spike data in 3 steps using Spike SDK for iOS platform.
All Spike SDK async method calls should be wrapped into try catch block.
## Step 1: Create a Spike Connection
To set up the Spike SDK create `SpikeConnectionV3` instance with your Spike application id, application user id and signature unique to each of your application users (more on generating signatures [here](/api-docs/authentication)):
```swift theme={null}
import SpikeSDK
static func createConnectionAPIv3(
applicationId: Int,
signature: String,
endUserId: String
) async throws -> SpikeConnectionAPIv3
)
```
## Step 2: Ask User for Permissions
Provide permissions to access iOS HealthKit data. Spike SDK method will check required permissions and request them if needed. Permission dialog may not be shown according to iOS permissions rules.
```swift theme={null}
try await spikeConnection.requestPermissions(forStatistics: [
.stepsTotal,
.distanceWalking,
]
)
```
## Step 3: Get Data
Info: The maximum permitted date range is 90 days
There are four types of data you can retrieve from Spike:
* **Statistics** are calculated values derived from records.
* **Activities** are data about a user's activities or workouts.
* **Sleep** is data about a user's sleep.
* **Records** consist of the raw data points collected from user devices or applications.
#### Statistics
Get daily statistics for steps and total distance from Apple Health:
```swift theme={null}
func getStatistics(
ofTypes types: [StatisticsType.steps],
from: Date.now.addingTimeInterval(-60 * 60 * 24),
to: Date.now,
interval: StatisticsInterval.hour,
filter: StatisticsFilter(providers: [.apple])
) async throws -> [Statistic]
```
where:
```swift theme={null}
public enum StatisticsType: String {
case steps
case distanceTotal = "distance_total"
case distanceWalking = "distance_walking"
case distanceCycling = "distance_cycling"
case distanceRunning = "distance_running"
case caloriesBurnedTotal = "calories_burned_total"
case caloriesBurnedBasal = "calories_burned_basal"
case caloriesBurnedActive = "calories_burned_active"
}
public enum StatisticsInterval: String, Codable {
case hour
case day
}
public struct StatisticsFilter {
public var excludeManual: Bool = false
public var providers: [Provider]? = nil
}
// Result:
public struct Statistic: Codable, Hashable {
public var start: Date
public var end: Date
public var duration: Int
public var type: StatisticsType
public var value: Double
public var unit: Unit
public var rowCount: Int?
public var recordIds: [UUID]?
}
```
#### Records
```swift theme={null}
func getRecords(
ofTypes types: [MetricType.stepsTotal],
from: Date.now.addingTimeInterval(-60 * 60 * 24),
to: Date.now,
filter: StatisticsFilter(providers: [.apple])
) async throws -> [Record]
```
where:
```swift theme={null}
public enum MetricType: String, Codable, Hashable {
case stepsTotal
case distanceTotal
case distanceWalking
case distanceCycling
case distanceRunning
case caloriesBurnedActive
case caloriesBurnedBasal
case caloriesBurnedTotal
}
// Result
public struct Record: Codable, Hashable {
public var recordId: UUID
public var inputMethod: InputMethod?
public var startAt: Date
public var endAt: Date?
public var modifiedAt: Date
public var duration: Int?
public var provider: Provider?
public var providerSource: ProviderSource?
public var isSourceAggregated: Bool?
public var source: RecordSource?
public var metrics: [String: Double]?
public var activityTags: [ActivityTag]?
public var activityType: ActivityType?
public var sessions: [ActivityEntry]?
public var laps: [ActivityEntry]?
public var segments: [ActivityEntry]?
public var splits: [ActivityEntry]?
public var samples: [ActivitySamples]?
public var routePoints: [ActivitySamples]?
}
```
# React Native SDK Background Delivery
Source: https://docs.spikeapi.com/sdk-docs/react-native/background-delivery
Background delivery ensures that data updates are sent to your backend via webhooks, even when the application is in the background or closed.
## iOS
### Important Notes About Background Delivery on iOS
* For most data types, the most possible frequency of updates is 1 hour.
* iOS can update data more frequently for some data types, for example, vo2 max.
* iOS may throttle the frequency of updates for background delivery depending on the app's activity, battery state, etc.
* Background delivery is not possible while a device is locked, so it will be executed only when the device is unlocked.
* iOS may stop background delivery if it detects that the app is not active for a long time.
* The feature is available starting with iOS 15.
**Important:** The Spike SDK, along with any other HealthKit applications, cannot guarantee data synchronization on a fixed schedule. The hourly sync
interval serves as a guideline rather than a strict requirement enforced by iOS. Consequently, the actual synchronization frequency may vary,
occurring hourly, once per day, or during specific system-defined events, such as the conclusion of Sleep Mode or when the device begins charging.
### Setup
#### Enable Background Delivery for the Application Target
* Open XCode with your ios project
* Open the folder of your project in Xcode
* Select the project name in the left sidebar
* Open the Signing & Capabilities section
* Select HealthKit background delivery under the HealthKit section
#### Initialization at Application Startup
You can skip this step if you are using Expo.
For background delivery to work properly, you need to initialize the Spike SDK at app startup.
`AppDelegate.swift`:
```swift theme={null}
import SpikeSDK
...
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
...
Spike.configure()
...
}
...
```
or `AppDelegate.mm`:
```text theme={null}
#import
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
[Spike configure];
...
}
```
## Android
### Important Notes About Background Delivery on Android
* Background delivery is scheduled to run every hour, but ultimately, Android decides when the delivery will be executed.
* Android may throttle the frequency of updates for background delivery depending on the app's activity, battery state, etc.
* Android may stop background delivery if it detects that the app is not active for a long time.
* There is a limit of queries that can be done in Health Connect, and it is different for foreground and background reads, so please request only essential data to be delivered in the background. More information in Health Connect documentation.
**Important**: The Spike SDK, along with any other applications, cannot guarantee data synchronization on a fixed schedule. The hourly sync interval serves as a guideline rather than a strict requirement enforced by Android. Consequently, the actual synchronization frequency may vary, occurring hourly, once per day, or during specific system-defined events, such as the conclusion of Sleep Mode or when the device begins charging.
### Setup
Add the following permission to your AndroidManifest.xml:
```xml theme={null}
```
## React Native Specific
Add `includeBackgroundDelivery: true` when asking for permissions:
```javascript theme={null}
spikeConnection.requestHealthPermissions({
statisticTypes: statisticTypes,
includeBackgroundDelivery: true,
})
```
You can also ask for background delivery permission at the same time as other permissions.
Now you can enable background delivery:
```javascript theme={null}
try {
await spikeConnection.enableBackgroundDelivery({
statisticTypes: [StatisticsType.steps, StatisticsType.distanceTotal],
sleepConfigs: [new SleepConfig({
includeMetricTypes: [],
})],
})
toast.show("Background delivery enabled")
} catch (error) {
console.log(`${error}`);
toast.show(`${error}`);
} catch (error) {
console.log(`${error}`);
}
```
Keep in mind that calling `enableBackgroundDelivery()` will overwrite previous configuration. So you have to call it with all the data types you want in one call.
To check current configuration call `getBackgroundDeliveryConfig()` method.
To stop background delivery call `disableBackgroundDelivery()` method.
## Samsung Health
If Samsung Health Data is enabled before enabling background delivery, `enableBackgroundDelivery` will automatically enable Samsung Health Data background delivery.
# React Native SDK Changelog
Source: https://docs.spikeapi.com/sdk-docs/react-native/changelog
A changelog documenting version updates and changes of Spike SDK for React Native platform.
If after updating you get errors while building the iOS app, please do the following:
1\.
2. - delete old pods
3\. - install updated pods
4\. Open project in XCode and do a full clean: or
# Spike ReactNative SDK Changelog
## 4.7.13
* Removed the deprecated v1/v2 connection API
* Updated Android SDK to `4.7.12`
* Updated iOS SDK to `4.7.11`
## 4.6.13
* Updated Android SDK to `4.6.12`
* Updated iOS SDK to `4.6.11`
* Added nutrition support across HealthKit (iOS), Health Connect (Android), and Samsung Health Data (Android). You can now request nutrition permissions, backfill historical nutrition data, and include nutrition fields in background delivery by passing `nutritionalFields` to `requestHealthPermissions`, `backfill`, `enableBackgroundDelivery`, and `requestPermissionsFromHealthKitAndBackfill`.
```
await connection.requestHealthPermissions({
nutritionalFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.carbohydrateG,
NutritionalField.fatTotalG,
],
});
await connection.backfill({
nutritionalFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.carbohydrateG,
NutritionalField.fatTotalG,
],
days: 7,
});
await connection.enableBackgroundDelivery({
nutritionalFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.carbohydrateG,
NutritionalField.fatTotalG,
],
});
```
* `getNutritionRecords({ from, to })` now reads the latest local HealthKit / Health Connect / Samsung Health Data nutrition data before returning records when the corresponding integration is enabled.
* `BackgroundDeliveryConfig` now exposes the `nutritionalFields` currently registered for background delivery.
* Added `close()` on the v3 connection to disconnect from the SDK, release the native connection, and stop background delivery when it is enabled.
```
await connection.close();
```
* IMPORTANT! If after updating you get errors while building iOS app please do following:
1. `rm -rf ios/Pods/ ios/Podfile.lock` - delete old pods
2. `cd ios; pod install --repo-update` - install updated pods
3. Open project in XCode 26 and do a full clean: `Product -> Clean build folder` or `Shift + Command + K`
## 4.5.23
* Updated Android SDK to `4.5.22`
* Updated iOS SDK to `4.5.21`
* Added **backfill** on the v3 connection to upload historical health data for a chosen number of days. Call it after you have granted the relevant permissions (HealthKit on iOS; Health Connect and/or Samsung Health Data on Android).
```
await connection.backfill({
statisticTypes,
metricTypes,
activityConfigs: [activityConfig],
sleepConfigs: [sleepConfig],
});
```
* **iOS (HealthKit):** added `requestPermissionsFromHealthKitAndBackfill` — requests read permissions for the given categories, then starts a backfill. Data for today and yesterday is pushed before the call resolves; older days continue in the background.
```
await connection.requestPermissionsFromHealthKitAndBackfill({
statisticTypes,
metricTypes,
activityConfigs: [activityConfig],
sleepConfigs: [sleepConfig],
});
```
* **Android (Samsung Health Data):** added `requestPermissionsFromSamsungHealthDataAndBackfill` — enables Samsung Health Data integration, requests permissions for the given categories, then starts a backfill.
* **Android (Health Connect):** added `requestPermissionsFromHealthConnectAndBackfill` — enables Health Connect integration, requests permissions for the given categories, then starts a backfill.
* **iOS:** If the app fails to build after upgrading, remove `ios/Pods` and `ios/Podfile.lock`, then run `pod install --repo-update` in `ios`, and run clean from Xcode.
## 4.5.13
* Updated Android SDK to `4.5.12`
* Updated iOS SDK to `4.5.11`
* Added `RecordConfig` for configuring records queries:
* `includeSamples` - whether to include samples (raw data points) in the response
* Updated `getRecords` to accept an optional `RecordConfig` parameter:
```
connection.getRecords({
ofTypes: metricTypes,
from: startDate,
to: endDate,
filter: new StatisticsFilter({ excludeManual: true }),
config: new RecordConfig({ includeSamples: true })
})
```
* IMPORTANT! If after updating you get errors while building iOS app please do following:
1. `rm -rf ios/Pods/ ios/Podfile.lock` - delete old pods
2. `cd ios; pod install --repo-update` - install updated pods
3. Open project in XCode 26 and do a full clean: `Product -> Clean build folder` or `Shift + Command + K`
## 4.4.13
* Updated Android SDK to `4.4.12`
* Updated iOS SDK to `4.4.11`
* Updated `Unit` enum with new values:
* Added: `miles`, `mPerSec`, `fahrenheit`, `ms`, `seconds`, `degrees`, `sleepStage`, `secPerM`, `rpm`, `spm`, `breathsPerMin`, `g`, `lbs`, `st`, `mmHg`, `mLPerKgPerMin`, `uV`, `mgPerDl`, `w`
* Removed: `kmh`, `kmPerMin`
* Improved data communication with API
* Enhanced the `getSleep` endpoint.
Important:
* Dates provided are now interpreted as dates only; the time component is ignored when querying the API.
* The primary night sleep period is associated with the calendar date on which it ends.
## 4.3.233
* Fixed nutrition record data so that `nutritionalFields` keys in ingredients and records match the `NutritionalField` enum values. You can now look up values using `ingredient.nutritionalFields[NutritionalField.energyKcal]` and similar expressions as intended.
* Updated Android SDK to `4.3.142`
* Internal improvements
* Updated iOS SDK to `4.3.161`
* Better Nutrition Facts Label Recognition response parsing
## 4.3.223
* Nutrition record and ingredient helpers are now methods on utils objects instead of standalone exports.
* Use `NutritionRecordUtils.withIngredient(record, ingredient)` instead of `withIngredient(record, ingredient)`
* Use `NutritionRecordUtils.withoutIngredient(record, ingredient)` instead of `withoutIngredient(record, ingredient)`
* Use `NutritionRecordUtils.withServingSize(record, servingSize, unit)` instead of `withServingSize(record, servingSize, unit)`
* Use `NutritionRecordIngredientUtils.withNutritionalField(ingredient, field, value)` instead of `withNutritionalField(ingredient, field, value)`
* Use `NutritionRecordIngredientUtils.withoutNutritionalField(ingredient, field)` instead of `withoutNutritionalField(ingredient, field)`
* Use `NutritionRecordIngredientUtils.withScaledServingSize(ingredient, newServingSize)` instead of `withScaledServingSize(ingredient, newServingSize)`
## 4.3.213
* MetricType: added namespace with metric allowlists for API requests:
* `MetricType.availableForRecordsRequest` – metrics accepted by records API request
* `MetricType.availableForActivitiesRequest` – metrics accepted by activities API request
* `MetricType.availableForSleepRequest` – metrics accepted by sleep API request
* `MetricType.availableForPersonRequest` – metrics accepted by person API request
* Update iOS SDK to `4.3.151`:
* Improved data push to API
* Fixed workout digest generation for caching
* Improved client cache configuration
## 4.3.203
* Updated Android SDK to `4.3.132`:
* Samsung Health Data Integration - Major Expansion:
* Added support for reading and pushing the following data types from Samsung Health:
* Workouts (exercises)
* Sleep data
* Body composition data (weight, height, BMI, body fat)
* User profile data (weight, height, birth date, gender)
* Blood pressure measurements
* Skin temperature measurements
* Heart rate measurements
* Blood oxygen saturation (SpO2) measurements
* Body temperature measurements
* More efficient Samsung Health Data pushes
* Large Samsung Health Data pushes are now automatically split into smaller chunks for reliable delivery
* Large Health Connect pushes are now automatically split into smaller chunks for reliable delivery
## 4.3.193
* Added Nutrition Facts Label Recognition for extracting nutritional information from label photos.
For more information please visit our [documentation](https://docs.spikeapi.com).
* `recognizeNutritionFactsLabel({ imageBase64, config? })` - Analyze a nutrition facts label image and extract nutritional data
* Added methods for manual nutrition record management:
* `createNutritionRecord({ nutritionRecord })` - Insert a new nutrition record with custom data
* `replaceNutritionRecord({ nutritionRecord })` - Replace an existing nutrition record completely
* Added `NutritionRecordUtils` for creating and manipulating nutrition records:
* `NutritionRecordUtils.create(options)` - Create a new nutrition record with specified properties
* `withIngredient(record, ingredient)` - Add an ingredient to a record
* `withoutIngredient(record, ingredient)` - Remove an ingredient from a record
* `withServingSize(record, servingSize, unit)` - Update serving size and unit
* Added `NutritionRecordIngredientUtils` for creating and manipulating ingredients:
* `NutritionRecordIngredientUtils.create(options)` - Create a new nutrition record ingredient
* `withNutritionalField(ingredient, nutritionalField, value)` - Set a nutritional field value
* `withoutNutritionalField(ingredient, nutritionalField)` - Remove a nutritional field
* `withScaledServingSize(ingredient, newServingSize)` - Scale serving size and nutritional values proportionally
* Moved metadata out of `NutritionRecord` into `NutritionRecordAnalysisResult`
* `NutritionRecord` no longer contains `status` and `failureReason` fields
* These fields are now part of `NutritionRecordAnalysisResult`
* `NutritionRecordAnalysisResult` now has a `nutritionRecord` field containing the nutrition data
* Nutrition API methods now return `NutritionRecordAnalysisResult`:
* `analyzeNutrition()` - returns `NutritionRecordAnalysisResult`
* `getNutritionRecords()` - returns `NutritionRecordAnalysisResult[]`
* `getNutritionRecord()` - returns `NutritionRecordAnalysisResult | null`
* `updateNutritionRecordServingSize()` - returns `NutritionRecordAnalysisResult`
* `createNutritionRecord()` - returns `NutritionRecordAnalysisResult`
* `replaceNutritionRecord()` - returns `NutritionRecordAnalysisResult`
* Update Android SDK to `4.3.122`
* Update iOS SDK to `4.3.141`
* IMPORTANT! If after updating you get errors while building iOS app please do following:
1. `rm -rf ios/Pods/ ios/Podfile.lock` - delete old pods
2. `cd ios; pod install --repo-update` - install updated pods
3. Open project in XCode 26 and do a full clean: `Product -> Clean build folder` or `Shift + Command + K`
## 4.3.183
* Added Nutrition AI features for analyzing food images. For more information please visit our [documentation](https://docs.spikeapi.com).
* `analyzeNutrition({ imageBase64, consumedAt?, config? })` - Analyze food image synchronously and wait for results
* `submitNutritionForAnalysis({ imageBase64, consumedAt?, config? })` - Analyze food image asynchronously, returns record ID
* `getNutritionRecords({ from, to })` - Retrieve nutrition records for a date range
* `getNutritionRecord({ id })` - Get a specific nutrition record by ID
* `deleteNutritionRecord({ id })` - Delete a nutrition record
* `updateNutritionRecordServingSize({ id, servingSize })` - Update serving size for a nutrition record
* Added new `Provider`:
* luna
* Added new `ProviderSource` types:
* fitbitSkinTemperatureSummary
* garminWellnessSkinTemperatureSummary
* lunaSleep
* Removed `ProviderSource` types:
* fitbitUserActivitiesList
* fitbitUserActivitiesDate
* fitbitUserActivitiesHeartDate
* fitbitUserActivitiesHeartDateInterday
* withingsMeasureGetWorkouts
* withingsMeasureGetActivity
* withingsMeasureGetMeas
* Removed `ActivityTag`:
* onBicycle
* Removed `Provider`:
* wahoo
* Update Android SDK to `4.3.112`
* Update iOS SDK to `4.3.131`
## 4.3.173
* Update Android SDK to `4.3.102`
* Enable Health Connect integration in the admin console right after enabling integration
* Enable Samsung Health Data integration in the admin console right after enabling integration
* Update iOS SDK to `4.3.121`
* Enable HealthKit integration in the admin console right after requesting permissions
## 4.3.163
* Added ability to read mindfulness activities:
```
spikeConnection.getActivities(
config: ActivityConfig(activityCategories: [ActivityCategory.mindfulness])
from: dateFrom,
to: dateTo
)
```
* Added ability to check Android Health Connect features availability:
```
const isMindfulessAvailable = spikeConnection.isHealthConnectFeatureAvailable(HealthConnectFeature.FEATURE_MINDFULNESS_SESSION)
```
* Added new ActivityTag types:
* mindfulness
* Added new ActivityType types:
* ecgMeasurement
* triathlon
* biathlon
* duathlon
* rollerblading
* skateboarding
* skating
* calisthenics
* weightLifting
* canoeing
* floorball
* jiuJitsu
* diving
* orienteering
* bootcamp
* motorsports
* horseriding
* paragliding
* multisport
* bloodTest
* mindfulnessSession
* Added new ProviderSource types:
* appleHealthkitMindfulness
* healthConnectMindfulnessSession
* Added a new ActivityCategory type:
* mindfulness
* Update Android SDK to `4.3.92`
* Updated Health connect to `1.1.0-rc03`
* Updated compileSdk to `36`
* Updated gradle to `8.13`
* Update iOS SDK to `4.3.111`
## 4.3.153
* Added documentation
## 4.3.143
* Fixed building iOS on Expo 54+ with New Architecture
* Updated Expo android manifest script
* Update Android SDK to `4.3.82`:
* Workaround for the Android 15 bug to make background delivery more resilient
* More efficient pushes to Spike API
## 4.3.133
* New `Spike.setLogCallback()` method to receive logs from the native SDK:
```
await Spike.setLogCallback((level, message) => {
if (level === LogLevel.verbose) {
return
}
console.log(`[SpikeSDK.${level.toUpperCase()}] ${message}`);
});
```
* Fixed `getBackgroundDeliveryConfig()` method to be `async getBackgroundDeliveryConfig(): Promise`
* Update Android SDK to `4.3.72`:
* Allow reading `SPO2` and `SWIMMING_LENGTHS` in `getActivities`
* Allow reading `SPO2` and `SKIN_TEMPERATURE` in `getSleeps`
* Update iOS SDK to `4.3.101`:
* Improved how sleep data is read for sleep score
## 4.3.123
* Update Android SDK to `4.3.62`:
* Optimized background delivery
## 4.3.113
* Added `.coros` `Provider`
* Updated `ProviderSource` enum
* Update Android SDK to `4.3.52`:
* Added `Provider.COROS`
* Updated `ProviderSource` enum
* Added new Statistic types:
* STRESS\_SCORE
* RECOVERY\_SCORE
* ACTIVITY\_SCORE
* Added new Metric types for `getSleep` request:
* SLEEP\_DURATION
* SLEEP\_DURATION\_AWAKE
* SLEEP\_DURATION\_DEEP
* SLEEP\_DURATION\_LIGHT
* SLEEP\_DURATION\_NAP
* SLEEP\_DURATION\_REM
* SLEEP\_EFFICIENCY
* SLEEP\_INTERRUPTIONS
* SLEEP\_LATENCY
* SLEEP\_SCORE
* Update iOS SDK to `4.3.91`:
* Added `.coros` `Provider`
* Updated `ProviderSource` enum
* Improved the way data is sent to API to reduce the size and speed of calls to Spike
* Added new Statistic types:
* stressScore
* recoveryScore
* activityScore
* Added new Metric types for `getSleep` request:
* sleepDuration
* sleepDurationAwake
* sleepDurationDeep
* sleepDurationLight
* sleepDurationNap
* sleepDurationRem
* sleepEfficiency
* sleepInterruptions
* sleepLatency
* sleepScore
* Better keychain compatibility
NOTE: If you use the keychain in your app with a service name equal to your bundle
identifier, please check if it contains key named `spikeApiToken` and delete it.
## 4.3.103
* New metric type:
* `glucose`
* Update Android SDK to `4.3.42`:
* New `MetricType.GLUCOSE` available in both Health Connect and Samsung Health
* Update Samsung Health Data SDK to version 1.0.0 (available only on Android 29+)
* Update iOS SDK to `4.3.81`:
* Added `MetricType`: `.glucose`
## 4.3.93
* The old `SpikeSDK` API has been deprecated
* Added `disableHealthKitIntegration` and `isHealthKitIntegrationEnabled` functions for iOS HealthKit
* Update Android SDK to `4.3.32`:
* Switch to protobuf java-lite 3.25.5 for better compatibility with firebase
* The old `SpikeSDK` API has been deprecated
* Update iOS SDK to `4.3.71`:
* The old `SpikeSDK` API has been deprecated
* Added `disableHealthKitIntegration` and `isHealthKitIntegrationEnabled` functions in spike connection
## 4.3.83
* New providers added: dexcom, freestyleLibre, huawei, strava
* Added .distanceSwimming into the list of metrics available in activities
* Updated native iOS SDK Version to `4.3.61`
* Updated native Android SDK Version to `4.3.22`
* Fixed prop names in SpikeRecord and Statistic data models
## 4.3.73
* Updated React native to 0.79
* Enhanced Expo support:
* Added a plugin to automatically configure the Android app by updating the necessary fields in `build.gradle` files and `AndroidManifest.xml`, including handling specified permissions.
* Enabled configuration of iOS HealthKit permission texts directly from the plugin settings.
* Added support for enabling background delivery via plugin configuration.
Config entry example for `app.json`:
```
[
"react-native-spike-sdk",
{
"ios": {
"healthSharePermission": "$(PRODUCT_NAME) needs access to read your health data",
"healthUpdatePermission": "$(PRODUCT_NAME) needs permission to save health data",
"isBackgroundDeliveryEnabled": true
},
"android": {
"healthConnectPermissions": [
"android.permission.health.READ_BASAL_METABOLIC_RATE",
"android.permission.health.READ_WEIGHT"
],
"isBackgroundDeliveryEnabled": true
}
}
]
```
## 4.3.63
* Added Samsung Health Integration on Android
* Updated native Android SDK Version to `4.3.12`:
* New statistics for Health Connect:
* HEARTRATE
* HEARTRATE\_MAX
* HEARTRATE\_MIN
* New metrics for Health Connect:
* HEARTRATE
* HEARTRATE\_MAX
* HEARTRATE\_MIN
* Improved transport protocol for even faster requests to Spike API
* Added Samsung Health Integration for:
* `StatisticType`s:
* STEPS
* DISTANCE\_TOTAL
* CALORIES\_BURNED\_ACTIVE
* CALORIES\_BURNED\_TOTAL
* CALORIES\_BURNED\_BASAL
* Added new `Provider`:
* SAMSUNG\_HEALTH\_DATA
* Added new `ProviderSource`:
* SAMSUNG\_HEALTH\_DATA\_AGGREGATION
* Renamed `MetricType`s:
* SLEEP\_BREATHING\_RATE to BREATHING\_RATE
* SLEEP\_BREATHING\_RATE\_MIN to BREATHING\_RATE\_MIN
* SLEEP\_BREATHING\_RATE\_MAX to BREATHING\_RATE\_MAX
* Updated native iOS SDK Version to `4.3.51`:
* Added new `Provider`:
* samsungHealthData
* Added new `ProviderSource`:
* samsungHealthDataAggregation
* Renamed `MetricTypes`:
* `sleepBreathingRate` to `breathingRate`
* `sleepBreathingRateMin` to `breathingRateMin`
* `sleepBreathingRateMax` to `breathingRateMax`
## 4.3.53
* Catch all exception types in the Android module
## 4.3.43
* Added missing parameter in `getHealthConnectPermissions`
## 4.3.33
* Updated native iOS SDK Version to `4.3.31`
* Improve how statistics are read from HealthKit
* New metric types:
* bodyFat
* bodyFatMax
* bodyFatMin
* bodyBoneMass
* bodyMassIndex
* bloodPressureSystolic
* bloodPressureSystolicMax
* bloodPressureSystolicMin
* bloodPressureDiastolic
* bloodPressureDiastolicMax
* bloodPressureDiastolicMin
* New fields in `UserProperties`:
* bodyBoneMass
* bodyFat
* bodyMassIndex
* Updated `getIntegrationInitUrl`. Now it accepts `IntegrationInitConfig` config object where you can pass:
* `redirectUri`: will override the one set in admin console
* `state`: when the authorization server redirects back to the client, it includes the `state` value originally sent
* `providerUserIdentifier`: at the moment used (and required) only when integrating with ultrahuman
* New provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider: .ultrahuman, config: IntegrationInitConfig(providerUserIdentifier: "user@mail.com"))
```
* Improved transport protocol for background delivery
* Updated native Android SDK Version to `4.2.72`
* New `MetricType`s added:
* BODY\_FAT
* BODY\_FAT\_MAX
* BODY\_FAT\_MIN
* BODY\_BONE\_MASS
* BODY\_MASS\_INDEX
* BLOOD\_PRESSURE\_SYSTOLIC
* BLOOD\_PRESSURE\_SYSTOLIC\_MIN
* BLOOD\_PRESSURE\_SYSTOLIC\_MAX
* BLOOD\_PRESSURE\_DIASTOLIC
* BLOOD\_PRESSURE\_DIASTOLIC\_MIN
* BLOOD\_PRESSURE\_DIASTOLIC\_MAX
* New fields in `UserProperties`:
* BODY\_FAT
* BODY\_BONE\_MASS
* BODY\_MASS\_INDEX
* Updated `getIntegrationInitUrl`. Now it accepts `IntegrationInitConfig` config object where you can pass:
* `redirectUri`: will override the one set in admin console
* `state`: when the authorization server redirects back to the client, it includes the `state` value originally sent
* `providerUserIdentifier`: at the moment used (and required) only when integrating with ultrahuman
* New provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide Ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider = Provider.ULTRAHUMAN, config = IntegrationInitConfig(providerUserIdentifier = "user@mail.com"))
```
* Updated native iOS SDK Version to `4.3.41`
* IntegrationInitConfig is now codable
* Additional check in `getIntegrationInitUrl` for email in ultrahuman integration
* Updated native Android SDK Version to `4.2.82`
* Added `IntegrationInitConfigUtils` for usage in Flutter and React Native libraries
* Additional check in `getIntegrationInitUrl` for email in ultrahuman integration
* New provider: `ultrahuman`
When integrating with Ultrahuman, you have to provide ultrahuman user email in `IntegrationInitConfig.providerUserIdentifier`. Example:
```
spikeConnection.getIntegrationInitUrl(provider: .ultrahuman, config: {providerUserIdentifier: "user@mail.com"})
```
## 4.3.23
* Updated native iOS SDK Version to `4.3.21`
* Improve how statistics are read from HealthKit
* Updated native Android SDK Version to `4.2.62`
* Added consumer proguard rules
## 4.3.13
* Updated native iOS SDK Version to `4.3.11`
* Improved transport protocol for even faster requests to Spike API
* New statistics: heartrate, heartrateMax, heartrateMin, heartrateResting
* Updated native Android SDK Version to `4.2.52`
* New statistics:
* HEARTRATE\_RESTING
* SLEEP\_DURATION\_TOTAL
* New statistics (only from non-HealthConnect providers):
* HEARTRATE
* HEARTRATE\_MIN
* HEARTRATE\_MAX
* Metric types updates
* Better proguard settings for uniqueness of generated class names
* Fix for statistics in different time zones
## 4.2.73
* Updated readme file
## 4.2.63
* Updated native iOS SDK Version to iOS 4.2.41
* Fix date format in JSON push
## 4.2.53
* Fixed typo in method name: `getGrantedHealthKitPermissions` in now properly called `getGrantedHealthConnectPermissions`
## 4.2.43
* Add support for React Native New Architecture (interop)
## 4.2.33
* New metrics:
* spo2
* bodyTemperature
* skinTemperature (available only on getSleep())
* sleepBreathingRate (available only on getSleep())
* sleepBreathingRateMin (available only on getSleep())
* sleepBreathingRateMax (available only on getSleep())
* sleepBreathingRateAvg (available only on getSleep())
* Updated native iOS SDK Version to iOS 4.2.31
* Updated native Android SDK Version to 4.2.42
* Added sleepScore property in the Record type
## 4.2.23
* New metric: `vo2Max` available in `getRecords` and `getActivities`
* Changed hrv metric names: `hrvRmssd`, `hrvSdnn`
* New statistic: `sleepScore`
* Updated native iOS SDK Version to iOS 4.2.21
* New `getUserProperties` method for reading: weight, height, timezone, birthdate, gender
* Updated native Android SDK Version to 4.2.32
* New `getUserProperties` method for reading: weight, height, timezone from Health Connect
* New `getUserProperties` method for reading: birthdate, gender from other providers
## 4.2.13
* Android Spike SDK: 4.2.12
* iOS Spike SDK: 4.2.11
* 🔴 Breaking Change: Update to `Spike.createConnectionAPIv3`. The method
`Spike.createConnectionAPIv3({appId:, authToken:, customerEndUserId:})` has been updated to:
`Spike.createConnectionAPIv3({applicationId:, signature:, endUserId:})`
* The `applicationId` parameter must now be provided as an **`number`**.
* The `signature` parameter now requires an **`HMAC-SHA256` signed user ID**.
* ⚠️ **Security Notice:**
* **Do not store your HMAC signing key within the application itself**, as this poses a security risk.
* Instead, generate and provide the signature from your backend.
* **Legacy Support**: For **development purposes only**, the previous connection flow remains available under the renamed method:
`Spike.createConnectionAPIv3Legacy({appId:, authToken:, customerEndUserId:})`
## 4.1.23
Fixed TypeScript definitions
## 4.1.13
* Background delivery
* Android Spike SDK: 4.1.12
* Add background delivery (see documentation for more information)
* Health Connect library updated to 1.1.0-alpha11
* Compile SDK and target SDK updated to 35
* Updated kotlin version to 1.9.25
* Updated other dependencies
* Add log callback
* Update gradle to 8.8.0
* iOS Spike SDK: 4.1.11
* Add background delivery
* Add log callback
* Send more sleep data
## 4.0.23
* Android Spike SDK: 4.0.22
* iOS Spike SDK: 4.0.11
## 4.0.13
* Android Spike SDK: 4.0.12
* iOS Spike SDK: 4.0.11
* Completely new SDK! Please see our official documentation for more details and usage instructions
## 2.5.8
* iOS Spike SDK: 2.4.5
* Android Spike SDK: 3.1.6
* Android: Update steps intraday to better check for manual entries
* Android: Add permissions specific to steps\_intraday
## 2.5.7
* iOS Spike SDK: 2.4.5
* Android Spike SDK: 3.1.5
* Android: check for permissions before reading additional sleep data
## 2.5.6
* iOS Spike SDK: 2.4.5
* Android Spike SDK: 3.1.4
* Android: update to the newest SDK version
## 2.5.5
* Android Spike SDK: 3.1.1
* iOS Spike SDK: 2.4.5
* iOS: update to the newest SDK version
## 2.5.4
* Android Spike SDK: 3.1.1
* iOS Spike SDK: 2.4.4
* iOS: Read more distance fields in a workout object from HealthKit
## 2.5.3
* Android Spike SDK: 3.1.1
* iOS Spike SDK: 2.4.2
* Pin iOS SDK version
## 2.5.2
* iOS Spike SDK: 2.4.2
* iOS: Add customer user id validation
* Android Spike SDK: 3.1.1
* Android: Better way to authenticate with API
* Android: Add saved session validation before connection is established
* Android: Add customer user id validation.
## 2.5.1
* Android Spike SDK: 3.0.19
* iOS Spike SDK: 2.4.1
* iOS: Fix an issue when the timezone changes while the app is running.
## 2.5.0
* Android Spike SDK: 3.0.19
* iOS Spike SDK: 2.4.0
* iOS: Add ECG data type.
## 2.4.5
* iOS Spike SDK: 2.3.2
* Android Spike SDK: 3.0.19
* Android: Fix an issue caused by calling `SpikeSDK` methods from different threads at the same time.
## 2.4.4
* Android Spike SDK: 3.0.19
* iOS Spike SDK: 2.3.2
* iOS: Fix an issue caused by calling `SpikeSDK` methods from different threads at the same time.
## 2.4.3
* iOS Spike SDK: 2.3.2
* Android Spike SDK: 3.0.19
* Add Background deliveries support for Expo-managed projects.
## 2.4.2
* Android Spike SDK: 3.0.19
* iOS Spike SDK: 2.3.2
* iOS: Add trigger extractAndPostData request done from the background.
## 2.4.1
* Android Spike SDK: 3.0.19
* iOS Spike SDK: 2.3.1
* iOS: Send cycling parameters with Activities stream data
* PROD & DEV environments are no longer supported.
## 2.3.7
* iOS Spike SDK: 2.2.3
* Android Spike SDK: 3.0.19
* Android: Add new logic for detecting changes in intraday\_steps metadata.
* Android: Add safeguard for to handle multiple sources of sleep data and avoid illogical values.
## 2.3.6
* Android Spike SDK: 3.0.17
* iOS Spike SDK: 2.2.3
* Add providerTimestamp to Activities Summary data model.
## 2.3.5
* Android Spike SDK: 3.0.17
* Android: Remove the limitation that prevented values from being sent when the requested period is more than a day.
* Android: Now calculating stage time by stages in sleep data.
* iOS Spike SDK: 2.2.2
* iOS: Include intraday entries of a Steps intraday data type for range requests.
## 2.3.3
* Android Spike SDK: 3.0.15
* iOS Spike SDK: 2.2.1
* Add a metadata object for Spike intraday entries.
## 2.3.2
* iOS Spike SDK: 2.2.0
* Android Spike SDK: 3.0.11
* Android: add safeguards for edge cases when a function could be called without necessary data.
## 2.3.1
* iOS Spike SDK: 2.2.0
* Android Spike SDK: 3.0.11
* Android: improve Android wrapper.
## 2.3.0
* Android Spike SDK: 3.0.11
* iOS Spike SDK: 2.2.0
* iOS: SpikeData wrapper model aligned with server.
## 2.2.11
* Android Spike SDK: 3.0.10
* iOS Spike SDK: 2.1.14
* iOS: Fix date range parsing.
## 2.2.10
* iOS Spike SDK: 2.1.13
* Android Spike SDK: 3.0.10
* Android: Step intraday data improvements.
## 2.2.9
* Android Spike SDK: 3.0.8
* iOS Spike SDK: 2.1.13
* Step intraday data improvements.
## 2.2.8
* Android Spike SDK: 3.0.8
* iOS Spike SDK: 2.1.12
* iOS: Step intraday data improvements.
## 2.2.7
* iOS Spike SDK: 2.1.11
* Android Spike SDK: 3.0.8
* Allow requesting multiple permission for Android using `requestHealthPermissions` method. Provide a single Spike data type or array of Spike data types.
## 2.2.6
* Android Spike SDK: 3.0.8
* iOS Spike SDK: 2.1.11
* iOS: Add a method to check Health Store data availability (isHealthDataAvailable).
## 2.2.5
* iOS Spike SDK: 2.1.10
* Android Spike SDK: 3.0.8
* Android: Can get permission contract before SpikeConnection is created.
* Android: Update package checker function to avoid unnecessary check for Android 14 and up versions.
## 2.2.4
* iOS Spike SDK: 2.1.10
* Android Spike SDK: 3.0.6
* Android: Move permission checking before data extraction to the SDK connection layer
## 2.2.3
* iOS Spike SDK: 2.1.10
* Android Spike SDK: 3.0.5
* Android: Extracting heart data fixed
## 2.2.2
* iOS Spike SDK: 2.1.10
* Android Spike SDK: 3.0.4
* Android: Added support for Android 14.
* Android: Fixed some issues with data requesting for a specified date range.
## 2.2.1
* Android Spike SDK: 3.0.2
* iOS Spike SDK: 2.1.10
* iOS: Spike SDK now uses ISO8601 standardized Calendar.
## 2.2.0
* iOS Spike SDK: 2.1.9
* Android Spike SDK: 3.0.2
* Android: Activity summary and activity stream can be called without granting all their permissions. Returned data depends on which permissions were provided.
* Android: Android Permission requests are divided by data types.
* SpikeConnection method `checkPermissionsGranted` now requires Spike data type.
* SpikeConnection method `requestHealthPermissions` now requires Spike data type.
See more: [Permissions section](/sdk-docs/rn/setup#android-permissions)
* Android: Works up to Android 13.
# React Native SDK Logging
Source: https://docs.spikeapi.com/sdk-docs/react-native/logging
Getting logs for troubleshooting and debugging from Spike SDK for React Native platform.
## Code Examples
To receive logs from Spike SDK for React Native, use the following code preferably before creating a connection:
```javascript theme={null}
await Spike.setLogCallback((level, message) => {
if (level === LogLevel.verbose) {
return
}
console.log(`[SpikeSDK.${level.toUpperCase()}] ${message}`);
});
```
# React Native SDK Nutrition AI
Source: https://docs.spikeapi.com/sdk-docs/react-native/nutrition-ai
Analyze food images and retrieve nutritional information using AI-powered analysis in your React Native app.
## About
The Spike SDK provides a convenient interface for the [Nutrition AI API](/nutrition-ai/overview), allowing you to analyze food images directly from your React Native application. The SDK handles image encoding, API communication, and response parsing, making it easy to integrate nutritional analysis into your app.
All Spike SDK async methods return Promises. Use `try-catch` blocks or `.catch()` handlers for error handling. See [Error Handling](#error-handling) for details.
## Key Features
* **AI-Powered Analysis** — advanced computer vision for food identification and nutritional calculations
* **Flexible Processing** — choose between synchronous (wait for results) or asynchronous (background) processing
* **Base64 Support** — submit images as base64-encoded strings
* **Complete Record Management** — retrieve, update, and delete nutrition records
## Available Methods
| Method | Description |
| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `analyzeNutrition({ imageBase64, consumedAt?, config? })` | Submit food image for synchronous processing and wait for the analysis results |
| `submitNutritionForAnalysis({ imageBase64, consumedAt?, config? })` | Submit food image for asynchronous processing and get record ID immediately for polling afterwards |
| `getNutritionRecords({ from, to })` | Retrieve nutrition records for a datetime range |
| `getNutritionRecord({ id })` | Get a specific nutrition record by ID |
| `updateNutritionRecordServingSize({ id, servingSize })` | Update serving size for a nutrition record |
| `deleteNutritionRecord({ id })` | Delete a nutrition record by ID |
## Analyzing Food Images
### Synchronous Processing
Use synchronous analysis when you want to wait for the complete nutritional analysis before proceeding. This is ideal for scenarios where you need immediate results and can display a loading indicator.
```javascript theme={null}
import {
NutritionRecordAnalysisMode,
NutritionalField
} from '@anthropic/spike-react-native-sdk';
// Capture image from camera or gallery and convert to base64
const imageBase64 = // ... base64-encoded image data
try {
const record = await spikeConnection.analyzeNutrition({
imageBase64: imageBase64,
consumedAt: new Date(),
config: {
analysisMode: NutritionRecordAnalysisMode.precise,
countryCode: 'us',
languageCode: 'en',
includeNutriScore: true,
includeDishDescription: true,
includeIngredients: true,
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.fatTotalG,
NutritionalField.carbohydrateG
]
}
});
console.log(`Dish: ${record.dishName ?? 'Unknown'}`);
console.log(`Serving size: ${record.servingSize ?? 0} ${record.unit ?? 'g'}`);
console.log(`Calories: ${record.nutritionalFields?.['energy_kcal'] ?? 0}`);
} catch (error) {
console.error('Analysis failed:', error);
}
```
You can also call with minimal parameters (config is optional):
```javascript theme={null}
// Using defaults - only imageBase64 is required
const record = await spikeConnection.analyzeNutrition({
imageBase64: imageBase64
});
```
**Processing Time**: Synchronous processing takes some time depending on image complexity. Consider showing a loading indicator to users. If you see that the analysis is taking too long, the recommendation is to use asynchronous processing instead.
### Asynchronous Processing
Use asynchronous processing when you want an immediate response without waiting for the analysis to complete. Record ID is returned.
The image is processed in the background, and you can retrieve results later by requesting nutrition analysis using the record ID or receive them via webhook.
```javascript theme={null}
try {
// Submit image for background processing
const recordId = await spikeConnection.submitNutritionForAnalysis({
imageBase64: imageBase64,
consumedAt: new Date(),
config: {
analysisMode: NutritionRecordAnalysisMode.fast,
includeIngredients: true
}
});
console.log(`Analysis started. Record ID: ${recordId}`);
// Optionally, poll for results later
// Your backend will also receive a webhook when analysis completes
} catch (error) {
console.error('Failed to submit:', error);
}
```
#### Retrieving Results Asynchronously
After submitting an image for asynchronous processing, you can retrieve the results using the record ID. Check the processing status for completion success.
```javascript theme={null}
import { NutritionRecordStatus } from '@anthropic/spike-react-native-sdk';
// Check the status and get results
const record = await spikeConnection.getNutritionRecord({ id: recordId });
if (record) {
switch (record.status) {
case NutritionRecordStatus.completed:
console.log(`Analysis complete: ${record.dishName ?? 'Unknown'}`);
break;
case NutritionRecordStatus.processing:
console.log('Still processing...');
break;
case NutritionRecordStatus.pending:
console.log('Queued for processing...');
break;
case NutritionRecordStatus.failed:
console.log(`Analysis failed: ${record.failureReason ?? 'Unknown error'}`);
break;
case NutritionRecordStatus.unknown:
console.log('Unknown status. Please update SDK.');
break;
}
}
```
For real-time notifications, configure webhooks in your [admin console](https://admin.spikeapi.com/). Your backend will receive a webhook notification when the analysis completes. See [Asynchronous Processing](/nutrition-ai/async) for webhook implementation details.
## Configuration Options
Customize the analysis using `NutritionalAnalysisConfig`:
```javascript theme={null}
const config = {
// Analysis speed vs. precision
analysisMode: NutritionRecordAnalysisMode.precise, // 'precise' (default) or 'fast'
// Country ISO 3166-1 alpha-2 code in lowercase
countryCode: 'us',
// Language ISO 639-1 code in lowercase
languageCode: 'en',
// Include Nutri-Score rating (A-E)
includeNutriScore: true,
// Include dish description
includeDishDescription: true,
// Include detailed breakdown of ingredients
includeIngredients: true,
// Specify which nutritional fields to include
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.fatTotalG,
NutritionalField.carbohydrateG,
NutritionalField.fiberTotalDietaryG,
NutritionalField.sodiumMg
]
};
const record = await spikeConnection.analyzeNutrition({
imageBase64: imageBase64,
consumedAt: new Date(),
config: config
});
```
### `NutritionalAnalysisConfig`
```typescript theme={null}
interface NutritionalAnalysisConfig {
/** A preferred mode for the analysis. Default is 'precise'. */
analysisMode: NutritionRecordAnalysisMode | null;
/** Country ISO 3166-1 alpha-2 code in lowercase */
countryCode: string | null;
/** Language ISO 639-1 code in lowercase */
languageCode: string | null;
/** Include nutri-score label of the food. Default is false. */
includeNutriScore: boolean | null;
/** Include dish description of the food. Default is false. */
includeDishDescription: boolean | null;
/** Include ingredients of the food. Default is false. */
includeIngredients: boolean | null;
/**
* Include specific nutrition fields in the analysis report.
* By default, carbohydrate_g, energy_kcal, fat_total_g and protein_g will be included.
*/
includeNutritionFields: NutritionalField[] | null;
}
```
### Analysis Modes
```typescript theme={null}
enum NutritionRecordAnalysisMode {
fast = "fast",
precise = "precise"
}
```
| Mode | Description |
| --------- | ---------------------------------------------------------------------------- |
| `precise` | Uses advanced AI models for highest accuracy and detailed analysis (default) |
| `fast` | Uses optimized models for quicker processing with good accuracy |
### Default Nutritional Fields
If `includeNutritionFields` is not specified, only these basic fields are included:
* `energyKcal`
* `proteinG`
* `fatTotalG`
* `carbohydrateG`
See [Nutritional Fields Reference](/technical-references/nutritional_fields) for all available fields.
## Managing Nutrition Records
### List Records by Date Range
Retrieve all nutrition records within a specified date range:
```javascript theme={null}
const now = new Date();
const startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); // 7 days ago
const endDate = now;
try {
const records = await spikeConnection.getNutritionRecords({
from: startDate,
to: endDate
});
for (const record of records) {
const consumedAt = record.consumedAt ?? 'Unknown date';
const size = record.servingSize ?? 0;
const unit = record.unit ?? 'g';
console.log(`${consumedAt}: ${record.dishName ?? 'Unknown'} - ${size}${unit}`);
}
} catch (error) {
console.error('Failed to fetch records:', error);
}
```
### Get a Specific Record
Retrieve a single nutrition record by its ID:
```javascript theme={null}
try {
const record = await spikeConnection.getNutritionRecord({ id: recordId });
if (record) {
console.log(`Dish: ${record.dishName ?? 'Unknown'}`);
console.log(`Nutri-Score: ${record.nutriScore ?? 'N/A'}`);
// Access nutritional values
if (record.nutritionalFields?.['energy_kcal']) {
console.log(`Calories: ${record.nutritionalFields['energy_kcal']} kcal`);
}
// Access ingredients if included
record.ingredients?.forEach(ingredient => {
console.log(`- ${ingredient.name}: ${ingredient.servingSize}${ingredient.unit}`);
});
} else {
console.log('Record not found');
}
} catch (error) {
console.error('Failed to fetch record:', error);
}
```
### Update Serving Size
Adjust the serving size of an existing record. All nutritional values are automatically recalculated proportionally:
```javascript theme={null}
try {
const updatedRecord = await spikeConnection.updateNutritionRecordServingSize({
id: recordId,
servingSize: 200.0 // New serving size in grams
});
console.log(`Updated serving size: ${updatedRecord.servingSize ?? 0}${updatedRecord.unit ?? 'g'}`);
console.log(`Recalculated calories: ${updatedRecord.nutritionalFields?.['energy_kcal'] ?? 0}`);
} catch (error) {
console.error('Failed to update record:', error);
}
```
### Delete a Record
Permanently remove a nutrition record (success status is returned regardless record is found or not):
```javascript theme={null}
try {
await spikeConnection.deleteNutritionRecord({ id: recordId });
console.log('Record deleted successfully');
} catch (error) {
console.error('Failed to delete record:', error);
}
```
## Response Data
### `NutritionRecord`
The `NutritionRecord` interface contains the analysis results:
```typescript theme={null}
interface NutritionRecord {
/** Report record ID */
recordId: UUID;
/** Processing status */
status: NutritionRecordStatus;
/** Detected dish name */
dishName: string | null;
/** Detected dish description */
dishDescription: string | null;
/** Dish name translated to target language */
dishNameTranslated: string | null;
/** Dish description translated to target language */
dishDescriptionTranslated: string | null;
/** Nutri-Score known as the 5-Colour Nutrition label (A-E) */
nutriScore: string | null;
/** Reason for processing failure */
failureReason: string | null;
/** Serving size in metric units */
servingSize: number | null;
/** Metric unit (g for solids, ml for liquids) */
unit: NutritionalUnit | null;
/** Nutritional values as key-value pairs */
nutritionalFields: { [key: string]: number } | null;
/** List of detected ingredients with nutritional information */
ingredients: NutritionRecordIngredient[] | null;
/** Upload timestamp in UTC (ISO 8601) */
uploadedAt: string;
/** Update timestamp in UTC (ISO 8601) */
modifiedAt: string;
/** The UTC time when food was consumed (ISO 8601) */
consumedAt: string | null;
}
```
### `NutritionRecordStatus`
```typescript theme={null}
enum NutritionRecordStatus {
pending = "pending",
processing = "processing",
completed = "completed",
failed = "failed",
unknown = "_unknown"
}
```
### `NutritionalUnit`
```typescript theme={null}
enum NutritionalUnit {
g = "g", // grams
mg = "mg", // milligrams
mcg = "mcg", // micrograms
ml = "ml", // milliliters
kcal = "kcal", // kilocalories
unknown = "_unknown"
}
```
### `NutritionRecordIngredient`
```typescript theme={null}
interface NutritionRecordIngredient {
/** Ingredient name using LANGUAL standard terminology */
name: string;
/** Ingredient name translated to target language */
nameTranslated: string | null;
/** Serving size in metric units */
servingSize: number;
/** Metric unit (g for solids, ml for liquids) */
unit: NutritionalUnit;
/** Nutritional values as key-value pairs */
nutritionalFields: { [key: string]: number } | null;
}
```
### `NutritionalField`
Use this enum to specify which nutritional fields to include in the analysis:
```typescript theme={null}
enum NutritionalField {
energyKcal = "energy_kcal",
carbohydrateG = "carbohydrate_g",
proteinG = "protein_g",
fatTotalG = "fat_total_g",
fatSaturatedG = "fat_saturated_g",
fatPolyunsaturatedG = "fat_polyunsaturated_g",
fatMonounsaturatedG = "fat_monounsaturated_g",
fatTransG = "fat_trans_g",
fiberTotalDietaryG = "fiber_total_dietary_g",
sugarsTotalG = "sugars_total_g",
cholesterolMg = "cholesterol_mg",
sodiumMg = "sodium_mg",
potassiumMg = "potassium_mg",
calciumMg = "calcium_mg",
ironMg = "iron_mg",
magnesiumMg = "magnesium_mg",
phosphorusMg = "phosphorus_mg",
zincMg = "zinc_mg",
vitaminARaeMcg = "vitamin_a_rae_mcg",
vitaminCMg = "vitamin_c_mg",
vitaminDMcg = "vitamin_d_mcg",
vitaminEMg = "vitamin_e_mg",
vitaminKMcg = "vitamin_k_mcg",
thiaminMg = "thiamin_mg",
riboflavinMg = "riboflavin_mg",
niacinMg = "niacin_mg",
vitaminB6Mg = "vitamin_b6_mg",
folateMcg = "folate_mcg",
vitaminB12Mcg = "vitamin_b12_mcg"
}
```
## Error Handling
All nutrition methods return Promises that can reject with errors. Always use try-catch blocks or `.catch()` handlers:
```javascript theme={null}
try {
const record = await spikeConnection.analyzeNutrition({
imageBase64: imageBase64,
consumedAt: new Date()
});
// Handle success
} catch (error) {
if (error.code === 'INVALID_IMAGE') {
console.error('Invalid image format or size');
} else if (error.code === 'NETWORK_ERROR') {
console.error('Network error:', error.message);
} else if (error.code === 'UNAUTHORIZED') {
console.error('Authentication failed');
} else {
console.error('Error:', error.message);
}
}
```
### Common Error Scenarios
| Error | Cause |
| -------------------- | --------------------------------------- |
| Invalid image format | Image is not JPEG, PNG, or WebP |
| Image too large | Base64-encoded image exceeds 10MB |
| Image too small | Image is smaller than 512×512 pixels |
| Unauthorized | Invalid or expired authentication token |
| Analysis timeout | AI processing took too long |
| Unidentifiable | Non-food image |
## Image Guidelines
For optimal analysis results, guide your users to capture images that:
1. **Center the food** — capture the plate contents as the main subject
2. **Fill the frame** — ensure the meal occupies most of the image
3. **Use proper lighting** — natural or bright lighting works best
4. **Avoid obstructions** — remove packaging and minimize utensils in frame
5. **Skip filters** — avoid filters that alter the food's appearance
See [Image Guidelines](/nutrition-ai/overview#image-guidelines) for complete recommendations.
## Best Practices
### 1. Request Only What You Need
Each additional field, ingredient breakdown, or optional data increases processing time. Only request what your app actually uses:
```javascript theme={null}
// ❌ Don't request everything "just in case"
const config = {
includeIngredients: true,
includeNutriScore: true,
includeDishDescription: true,
includeNutritionFields: Object.values(NutritionalField) // All 29 fields
};
// ✅ Request only what you need
const config = {
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.carbohydrateG,
NutritionalField.fatTotalG
]
};
```
### 2. Consider your actual UI requirements:
* Do you display ingredients? If not, skip `includeIngredients`.
* Do you show Nutri-Score? If not, skip `includeNutriScore`.
* Which nutritional values do you actually display? Request only those.
### 3. Choose the Right Processing Mode
* **Synchronous** (`analyzeNutrition`): Use when you need immediate results and can show a loading state
* **Asynchronous** (`submitNutritionForAnalysis`): Use for better UX when you don't need immediate results, or when processing multiple images
### 4. Handle All Status Values
When using asynchronous processing, always check the record status before accessing results:
```javascript theme={null}
const record = await spikeConnection.getNutritionRecord({ id: recordId });
if (!record) {
// Handle not found
return;
}
if (record.status !== NutritionRecordStatus.completed) {
if (record.status === NutritionRecordStatus.failed) {
// Handle failure
console.error(`Failed: ${record.failureReason}`);
} else {
// Still processing
console.log(`Status: ${record.status}`);
}
return;
}
// Safe to access results
console.log(`Dish: ${record.dishName}`);
```
### 5. Implement Webhook Handling
For production apps using asynchronous processing, implement [webhook handling](/nutrition-ai/async) on your backend to receive real-time notifications when analysis completes.
### 6. Create Reusable Configuration
If you're using the same settings across your app, create a shared configuration helper:
```javascript theme={null}
// nutritionConfig.js
import { NutritionRecordAnalysisMode, NutritionalField } from '@anthropic/spike-react-native-sdk';
export const standardNutritionConfig = {
analysisMode: NutritionRecordAnalysisMode.precise,
includeIngredients: true,
includeNutriScore: true,
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG,
NutritionalField.fatTotalG,
NutritionalField.carbohydrateG,
NutritionalField.fiberTotalDietaryG
]
};
// Usage
import { standardNutritionConfig } from './nutritionConfig';
const record = await spikeConnection.analyzeNutrition({
imageBase64: imageBase64,
consumedAt: new Date(),
config: standardNutritionConfig
});
```
### 7. Use React State for Loading UI
```javascript theme={null}
import React, { useState } from 'react';
import { View, ActivityIndicator, Text } from 'react-native';
import { NutritionalField } from '@anthropic/spike-react-native-sdk';
function NutritionAnalyzer({ spikeConnection }) {
const [isLoading, setIsLoading] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const analyzeFood = async (imageBase64) => {
setIsLoading(true);
setError(null);
try {
const record = await spikeConnection.analyzeNutrition({
imageBase64: imageBase64,
consumedAt: new Date(),
config: {
includeNutritionFields: [
NutritionalField.energyKcal,
NutritionalField.proteinG
]
}
});
setResult(record);
} catch (err) {
setError(err.message);
} finally {
setIsLoading(false);
}
};
if (isLoading) {
return (
Analyzing your meal...
);
}
// Render result or error...
}
```
## Related Documentation
* [Nutrition AI Overview](/nutrition-ai/overview) — API overview and key features
* [Implementation Guide](/nutrition-ai/implementation) — Detailed API implementation patterns
* [Asynchronous Processing](/nutrition-ai/async) — Webhook configuration and handling
* [Nutritional Fields Reference](/technical-references/nutritional_fields) — Complete list of available nutritional fields
# React Native SDK Overview
Source: https://docs.spikeapi.com/sdk-docs/react-native/overview
Overview of SDK-based integrations with health platforms including common principles, integration creation process, and data extraction methods for mobile health data management.
# Common principles
SDK-based integrations (Apple Health Kit, Android Health Connect, and Samsung Health Data) have a few common principles that separate them from all other providers.
* Data is stored only on the device (phone) therefore, requires to be actively extracted
* Permission control is very gradual (each metric reading requires approval)
* Data becomes available instantly as it's recorded
* Integration requires active management through SDKs
# Creating integrations
Creating these integrations does not follow the OAuth path. Meaning
* there will be no redirecting to the provider authorization page,
* and therefore no callback to URL after permissions are granted.
* Permissions are granted locally, on the phone itself, by triggering SDK methods.
* Permissions are also managed by default OS schemas, application users navigating settings menus, are granted for application package (reinstalling the app might require reauthorization), can be revoked or ignored when requested by OS based on their policies.
The schema below should explain the flow and explain the sequence of events, the moment integration gets created.
# Data extraction
Data stored only on the user's mobile device's local hardware (encrypted at rest). To make data available over API, first you must select and call SDK functions dedicated to sleep, workouts or other metrics reading.
The schema below should explain the flow and explain the sequence of events, the moment when data becomes available for reading over API and SDK.
# Events sequence schema
# React Native SDK Plugin Expo
Source: https://docs.spikeapi.com/sdk-docs/react-native/plugin-expo
Using Expo plugin with Spike SDK for React Native in your project.
Use the [Expo config plugin](https://docs.expo.io/guides/config-plugins/) to configure the Spike SDK for React Native apps.
## Expo Installation
This package cannot be used in the "Expo Go" app because [it requires custom native code](https://docs.expo.dev/workflow/customizing/).
The plugin automatically configures both iOS and Android platforms for the Spike SDK. You can find more information
about what is being setup in [React Native setup guide](/sdk-docs/rn/setup).
First install the package with yarn, npm or [`npx expo install`](https://docs.expo.dev/more/expo-cli/#installation).
```sh theme={null}
yarn install react-native-spike-sdk
```
After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
```json theme={null}
{
"expo": {
"plugins": [
[
"react-native-spike-sdk",
{
"ios": {
"healthSharePermission": "Custom health share permission",
"healthUpdatePermission": "Custom health update permission",
"isBackgroundDeliveryEnabled": true
},
"android": {
"isBackgroundDeliveryEnabled": true,
"healthConnectPermissions": [
"android.permission.health.READ_STEPS",
]
}
}
]
]
}
}
```
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.dev/workflow/customizing/) guide.
## API
The plugin provides configuration options for both iOS and Android platforms. Every time you change the configuration or plugins, you'll need to rebuild (and `prebuild`) the native app. If no extra properties are added, defaults will be used.
### iOS Configuration
* `healthSharePermission` (*string*): Sets the iOS `NSHealthShareUsageDescription` permission message to the `Info.plist`. Defaults to `Allow $(PRODUCT_NAME) to check health info`.
* `healthUpdatePermission` (*string*): Sets the iOS `NSHealthUpdateUsageDescription` permission message to the `Info.plist`. Defaults to `Allow $(PRODUCT_NAME) to update health info`.
* `isBackgroundDeliveryEnabled` (*boolean*): Adds `com.apple.developer.healthkit.background-delivery` entitlement to the iOS project. Defaults to `true`.
### Android Configuration
* `isBackgroundDeliveryEnabled` (*boolean*): Whether to enable background health data deliveries. Defaults to `true`.
* `healthConnectPermissions` (*string\[]*): Array of Health Connect Android permissions to add to the manifest. Defaults to `[]`.
Extensive list of available permissions:
```text theme={null}
android.permission.health.READ_NUTRITION
android.permission.health.READ_ACTIVE_CALORIES_BURNED
android.permission.health.READ_TOTAL_CALORIES_BURNED
android.permission.health.READ_STEPS
android.permission.health.READ_DISTANCE
android.permission.health.READ_ELEVATION_GAINED
android.permission.health.READ_RESTING_HEART_RATE
android.permission.health.READ_HEART_RATE_VARIABILITY
android.permission.health.READ_FLOORS_CLIMBED
android.permission.health.READ_BASAL_METABOLIC_RATE
android.permission.health.READ_SLEEP
android.permission.health.READ_HEART_RATE
android.permission.health.READ_EXERCISE
android.permission.health.READ_SPEED
android.permission.health.READ_POWER
android.permission.health.READ_OXYGEN_SATURATION
android.permission.health.READ_BLOOD_GLUCOSE
android.permission.health.READ_RESPIRATORY_RATE
android.permission.health.READ_WEIGHT
android.permission.health.READ_HEIGHT
android.permission.health.READ_BODY_FAT
android.permission.health.READ_LEAN_BODY_MASS
android.permission.health.READ_BODY_WATER_MASS
android.permission.health.READ_BODY_TEMPERATURE
android.permission.health.READ_BLOOD_PRESSURE
android.permission.health.READ_BONE_MASS
```
## Capabilities
This plugin will enable the iOS `com.apple.developer.healthkit` entitlement, but to sync this with the bundle identifier's production capabilities you'll need to do one of two things:
* Automatic: Build the app with [EAS build](https://docs.expo.io/build/introduction/)
* Manual: Visit [Apple developer portal](https://developer.apple.com/account/resources/identifiers/list) and enable the HealthKit capability for your bundle identifier before building for production. This can also be done via Xcode.
# React Native SDK Setup
Source: https://docs.spikeapi.com/sdk-docs/react-native/setup
This document provides a setup guide of the Spike SDK for React Native platform.
## Version
**Current React Native SDK Version:** `4.7.13`
## Resources
* React Native Package: [Available here](https://www.npmjs.com/package/react-native-spike-sdk)
* API Reference for Spike SDK: [Available here](https://spike_api.gitlab.io/spike-react-native-sdk)
* Example app: [Available here](https://gitlab.com/spike_api/public/spike-sdk-examples/-/tree/master/react-native-v3)
## Requirements
* **iOS Version**: `13.0+`.
* **Xcode 26** is required.
* **Android Version**: `9.0+` (Level 28, P, Pie)
## SDK Installation
Install the react-native-spike-sdk package from [npm](https://www.npmjs.com/package/react-native-spike-sdk):
```text theme={null}
yarn add react-native-spike-sdk
```
## iOS Setup Guide
Use `pod install` and `pod update` commands from `ios/` folder of your app to install/update `SpikeSDK`.
### iOS Signing & Capabilities
To add HealthKit support to your application's Capabilities.
* Open the `iOS/` folder of your project in Xcode
* Select the project name in the left sidebar
* Open the Signing & Capabilities section
* In the main view select '+ Capability' and double-click HealthKit
More details you can find [here](https://developer.apple.com/documentation/healthkit/setting_up_healthkit).
### Info.plist
Add HealthKit permissions descriptions to your `Info.plist` file:
```xml theme={null}
NSHealthShareUsageDescription
We will use your health information to better track workouts.
NSHealthUpdateUsageDescription
We will update your health information to better track workouts.
```
## Android Setup Guide
To add the SDK to your project, you have to add the following to your project's `build.gradle` file in the repositories block.
```gradle theme={null}
allprojects {
repositories {
// Other repositories
maven {
url 'https://gitlab.com/api/v4/projects/43396247/packages/maven'
}
}
}
```
### Android Permissions
Include the necessary health permissions in your AndroidManifest.xml to fully leverage the Spike SDK and access data from apps integrated with Health Connect. Please refer to [this guide](https://developer.android.com/health-and-fitness/guides/health-connect/get-started#declare-permissions) for details on the required permissions.
**Note**: Only request permissions essential to your app’s functionality. Requesting unused permissions may lead to app store rejections.
```xml theme={null}
```
Add an intent filter to your activity definition so that you can request the permissions at runtime.
```xml theme={null}
```
To handle Android 14, you also need to add activity-alias to your AndroidManifest.xml It is just a wrapper for the activity that requests permissions, so no real activity is necessary.
```xml theme={null}
```
# React Native SDK Usage Guide
Source: https://docs.spikeapi.com/sdk-docs/react-native/usage-guide
Start getting Spike data in 3 steps using Spike SDK for React Native platform.
## Step 1: Create a Spike Connection
To set up the Spike SDK create `SpikeConnectionV3` instance with your Spike application id, application user id and signature unique to each of your application users (more on generating signatures [here](/api-docs/authentication)):
```javascript theme={null}
import Spike from 'react-native-spike-sdk';
const connection = await Spike.createConnectionAPIv3({
applicationId: 1000,
signature: "signature",
endUserId: "user-id"
});
```
## Step 2: Ask User for Permissions
If you plan to use Android Health Connect and/or Apple HealthKit data providers you should first get users permission to use their data:
```javascript theme={null}
// This call may present user with OS native modal asking for read
// permissions needed to get steps and distance data. If you want to
// present it only once, you should list all the statistic types you
// plan to use. Otherwise, you can call this only before you actually
// want to use the data.
await spikeConnection.requestHealthPermissions({
statisticTypes: [StatisticsType.steps, StatisticsType.distanceTotal]
});
```
For Android there are more methods that let you get permissions granted by user, check if Health Connect is installed or should be updated, etc. If you are
OK with Spike SDK handling these, you can use `requestHealthPermissions` only. On iOS `requestHealthPermissions` is the only method that has to be called.
Keep in mind that you should do it before trying to read data from Android Health Connect or Apple HealthKit.
## Step 3: Get Data
The maximum permitted query date range on Android Health Connect is 90 days
There are 4 types of data you can retrieve from Spike:
* **Statistics** are calculated values derived from records.
* **Activities** are data about user's activities or workouts.
* **Sleep** is data about user's sleep.
* **Records** consist of the raw data points collected from user devices or applications.
### Statistics
Now you can read hourly statistics data of steps and distance for today:
```javascript theme={null}
const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
const statistics = await spikeConnection.getStatistics(
[StatisticsType.steps, StatisticsType.distanceTotal],
start,
end,
StatisticsInterval.hour,
new StatisticsFilter(false, [Provider.apple, Provider.healthConnect])
)
```
where:
```javascript theme={null}
export enum StatisticsType {
steps = "steps",
distanceTotal = "distance_total",
distanceWalking = "distance_walking",
distanceCycling = "distance_cycling",
distanceRunning = "distance_running",
caloriesBurnedTotal = "calories_burned_total",
caloriesBurnedBasal = "calories_burned_basal",
caloriesBurnedActive = "calories_burned_active",
sleepScore = "sleep_score",
sleepDurationTotal = "sleep_duration_total",
heartrate = "heartrate",
heartrateMax = "heartrate_max",
heartrateMin = "heartrate_min",
heartrateResting = "heartrate_resting",
unknown = "_unknown"
}
enum StatisticsInterval {
day,
hour
}
// Filter
class StatisticsFilter {
excludeManual: boolean = false
providers: Provider[] | undefined
activityTags: ActivityTag[] | undefined
constructor({
excludeManual = false,
providers = undefined,
activityTags = undefined
}: StatisticsFilterConstructorParameters) {
this.excludeManual = excludeManual;
this.providers = providers;
this.activityTags = activityTags;
}
}
// Result:
interface Statistic {
intervalStart: string;
intervalEnd: string;
intervalDuration: number;
statisticType: StatisticsType;
statisticValue: number;
statisticUnit: Unit;
rowCount: number | null;
recordIds: UUID[] | null;
}
```
### Records
```javascript theme={null}
const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1);
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
const records = await spikeConnection.getRecords({
ofTypes: metricTypes,
from: start,
to: end,
filter: new StatisticsFilter({
excludeManual: false,
providers: [Provider.apple, Provider.healthConnect]
})
})
```
```javascript theme={null}
enum MetricType {
heartrateMax = "heartrate_max",
heartrateMin = "heartrate_min",
heartrate = "heartrate",
heartrateResting = "heartrate_resting",
hrvRmssd = "hrv_rmssd",
hrvSdnn = "hrv_sdnn",
elevationMax = "elevation_max",
elevationMin = "elevation_min",
elevationGain = "elevation_gain",
elevationLoss = "elevation_loss",
ascent = "ascent",
descent = "descent",
caloriesBurnedActive = "calories_burned_active",
caloriesBurnedBasal = "calories_burned_basal",
caloriesBurned = "calories_burned",
caloriesIntake = "calories_intake",
stepsTotal = "steps",
floorsClimbed = "floors_climbed",
distanceTotal = "distance",
distanceWalking = "distance_walking",
distanceCycling = "distance_cycling",
distanceRunning = "distance_running",
distanceWheelchair = "distance_wheelchair",
distanceSwimming = "distance_swimming",
speed = "speed",
speedMax = "speed_max",
speedMin = "speed_min",
airTemperatureMax = "air_temperature_max",
airTemperatureMin = "air_temperature_min",
spo2 = "spo2",
spo2Max = "spo2_max",
spo2Min = "spo2_min",
longitude = "longitude",
latitude = "latitude",
elevation = "elevation",
durationActive = "duration_active",
swimmingLengths = "swimming_lenghts",
swimmingDistancePerStroke = "swimming_distance_per_stroke",
height = "height",
weight = "weight",
birthYear = "birth_year",
birthDate = "birth_date",
timezone = "timezone",
gender = "gender",
vo2Max = "vo2max",
bodyTemperature = "body_temperature",
skinTemperature = "skin_temperature",
breathingRate = "breathing_rate",
breathingRateMin = "breathing_rate_min",
breathingRateMax = "breathing_rate_max",
bodyFat = "body_fat",
bodyFatMax = "body_fat_max",
bodyFatMin = "body_fat_min",
bodyBoneMass = "body_bone_mass",
bodyMassIndex = "body_mass_index",
bloodPressureSystolic = "blood_pressure_systolic",
bloodPressureSystolicMin = "blood_pressure_systolic_min",
bloodPressureSystolicMax = "blood_pressure_systolic_max",
bloodPressureDiastolic = "blood_pressure_diastolic",
bloodPressureDiastolicMin = "blood_pressure_diastolic_min",
bloodPressureDiastolicMax = "blood_pressure_diastolic_max",
unknown = "_unknown"
}
// Result:
interface SpikeRecord {
recordId: UUID;
inputMethod: InputMethod | null;
startAt: string;
endAt: string | null;
modifiedAt: string;
duration: number | null;
providerSlug: Provider | null;
providerSource: ProviderSource | null;
isSourceAggregated: boolean | null;
source: RecordSource | null;
metrics: { [key: string]: number } | null;
sleepScore: number | null;
activityTags: ActivityTag[] | null;
activityType: ActivityType | null;
sessions: ActivityEntry[] | null;
laps: ActivityEntry[] | null;
segments: ActivityEntry[] | null;
splits: ActivityEntry[] | null;
samples: ActivityEntry[] | null;
routePoints: ActivityEntry[] | null;
sleep: ActivityEntry[] | null;
}
```
# React Native SDK for Samsung Health
Source: https://docs.spikeapi.com/sdk-docs/react-native/usage-guide-shd
Start getting Spike data in 3 steps using Spike SDK for Samsung Health Data on React Native platform.
## Requirements and Limitations
Samsung Health Data is available on Android devices only!
* Samsung Health Data SDK runs on devices with Android 10 (API level 29) or above. It is available on all Samsung smartphones and non-Samsung Android smartphones.
* Samsung Health Data SDK works with Samsung Health. Samsung Health version 6.30.2 or higher is required.
* Samsung Health Data SDK supports Java 17 or higher version.
* The emulator is not supported.
* Data obtained using Samsung Health Data SDK is for fitness and wellness information only. It is not for the diagnosis or treatment of any medical condition.
## Step 1: Create a Spike Connection
If you already set up [Android Health Connect integration](/sdk-docs/rn/usage-guide) in your app, you should skip to [this step](#step-2%3A-ask-user-for-permissions) and use the same Spike SDK connection object.
To set up the Spike SDK create `SpikeConnectionV3` instance with your Spike application id, application user id and signature unique to each of your application users (more on generating signatures [here](/api-docs/authentication)):
```javascript theme={null}
import Spike from 'react-native-spike-sdk';
const connection = await Spike.createConnectionAPIv3({
applicationId: 1000,
signature: "signature",
endUserId: "user-id"
});
```
## Step 2: Ask User for Permissions
If you want to read data from Samsung Health, you have to ensure the user gives your app permissions.
First, you have to check if Samsung Health is available on users' phone using
`checkSamsungHealthDataAvailability` method:
```typescript theme={null}
val availability = spikeConnection.checkSamsungHealthDataAvailability()
```
where
```typescript theme={null}
interface SamsungHealthDataAvailability {
status: SamsungHealthDataAvailabilityStatus;
errorCode: number;
message: string;
}
enum SamsungHealthDataAvailabilityStatus {
/**
* Samsung Health is not installed. Ask the user to install it.
*/
notInstalled = "NOT_INSTALLED",
/**
* The version of Samsung Health is too old. Ask users to update it.
*/
updateRequired = "UPDATE_REQUIRED",
/**
* The Samsung Health Data is installed but is disabled.
*/
disabled = "DISABLED",
/**
* Samsung Health has been installed, but the user didn't perform an initial process, such as
* agreeing to the Terms and Conditions.
*/
notInitialized = "NOT_INITIALIZED",
/**
* Samsung Health returned the other error.
*/
errorOther = "ERROR_OTHER",
/**
* Samsung Health Data is available.
*/
installed = "INSTALLED",
}
```
If Samsung Health is installed, you can ask user for permissions using `requestPermissions` method:
```typescript theme={null}
// Samsung Health integration has to be enabled in Spike SDK connection before
// using further methods for reading data or managing permissions:
spikeConnection.enableSamsungHealthDataIntegration()
spikeConnection.requestPermissionsFromSamsungHealthData(
statisticsTypes = setOf(StatisticsType.STEPS, StatisticsType.DISTANCE_TOTAL)
)
```
Please note that users might only grant partial permissions. In such cases, it’s up to you to decide whether your app can function effectively with limited access.
The Spike SDK itself will still operate even without full permissions; however, it may result in no data being returned in certain scenarios.
You can now use `StatisticsFilter(providers = listOf(Provider.SAMSUNG_HEALTH_DATA))` to specifically retrieve data from Samsung Health. Alternatively, you can omit the
providers parameter entirely and allow Spike to choose the most suitable data source based on your request.
## Step 3: Get Data
Reading data is the same as in Android from [Google Health Connect](/sdk-docs/android/usage-guide#step-3%3A-get-data). The only difference is that
you may want to filter results by `Provider.SAMSUNG_HEALTH_DATA` to get only Samsung Health data:
```typescript theme={null}
StatisticsFilter(providers = [Provider.SAMSUNG_HEALTH_DATA])
```
## Background Delivery
Samsung Health data can be delivered in the background the same way as Apple HealthKit or Android Health Connect.
If you want to use background delivery to get Samsung Health data, you have to enable Samsung Health Data integration first:
```typescript theme={null}
spikeConnection.enableSamsungHealthDataIntegration()
```
After enabling Samsung Health Data integration, you can use background delivery normally.
See the [React Native SDK Background Delivery section](/sdk-docs/rn/background-delivery) for more details.
## Developer Mode for Testing
To test Samsung Health Data integration on your phone, you have to enable developer mode
in the Samsung Health app:
1. Tap the ‘⋮’ button of Samsung Health in the top-right.
2. Select Settings > About Samsung Health.
3. Tap the version line region quickly 10 times or more.
If you are successful, the Developer mode (Samsung Health Data SDK) button is displayed.
4. Select Developer mode (Samsung Health Data SDK) .
5. Agree with the Notice about usage of the Developer mode.
6. To read data from Samsung Health with Samsung Health Data SDK, turn Developer Mode for Data Read on.
After your app is ready for release, you should apply for a Samsung Partnership Agreement. For more information, please contact Spike support.
The Samsung Health developer mode is ONLY intended for testing or debugging your app. It is NOT for app users. Do not provide a developer mode guide to app users.
## See Also
* [Samsung error codes](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-availability/error-code.html)
* [SamsungHealthDataPermissionManager](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-permission-manager/index.html)
* [SamsungHealthDataAvailabilityStatus](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3.samsung/-samsung-health-data-availability-status/index.html)
* [SpikeConnectionV3.enableSamsungHealthDataIntegration](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/enable-samsung-health-data-integration.html)
* [SpikeConnectionV3.disableSamsungHealthDataIntegration](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/disable-samsung-health-data-integration.html)
* [SpikeConnectionV3.isSamsungHealthDataIntegrationEnabled](https://spike_api.gitlab.io/spike-android-sdk/sdk/com.spikeapi.apiv3/-spike-connection-a-p-iv3/is-samsung-health-data-integration-enabled.html)
# Activity Types and Tags
Source: https://docs.spikeapi.com/technical-references/activity_matrix
This document provides a comprehensive overview of all activities, detailing their tags and descriptions. The table below summarizes the key features supported by each activity.
## Tags
Tags are utilized in queries to efficiently retrieve a set of activities that share common characteristics. The table below provides a list of all available tags, along with their descriptions and usage examples.
| **Tag** | **Description** |
| :------------------ | :---------------------------- |
| climbing | Climbing |
| cycling | Cycling |
| dancing | Dancing |
| ecg | ECG measurement |
| gym | Indoor or outdoor gym workout |
| individual\_sports | Individual sports |
| lab\_report | Lab report |
| martial\_arts | Martial arts |
| mindfulness | Mindfulness |
| on\_foot | Walking |
| outdoor\_recreation | Outdoor recreation |
| racket\_sports | Racket sports |
| running | Running |
| sleep | Sleep |
| swimming | Swimming |
| team\_sports | Team sports |
| walking | Walking |
| water\_sports | Water sports |
| wheelchair | Wheelchair |
| winter\_sports | Winter sports |
| workout | General workout |
## Activities
The table below provides details on the various activities, including a brief description of each and the associated tags.
| **Activity Type** | **Description** | **Tags** |
| :------------------------------ | :------------------------------- | :-------------------------------------------- |
| aerobics | | `workout` |
| aikido | Aikido | `martial_arts` `workout` |
| american\_football | American football | `team_sports` `workout` |
| archery | Archery | `individual_sports` `workout` |
| australian\_football | Australian football | `team_sports` `workout` |
| backcountry\_skiing | Backcountry skiing | `winter_sports` `workout` |
| backcountry\_snowboarding | Backcountry snowboarding | `winter_sports` `workout` |
| badminton | Badminton | `racket_sports` `workout` |
| barre | Barre | `dancing` `workout` |
| baseball | Baseball | `team_sports` `workout` |
| basketball | Basketball | `team_sports` `workout` |
| biathlon | Biathlon activity | `winter_sports` `workout` |
| blood\_test | Blood Test | `lab_report` |
| bmx\_cycling | BMX cycling activity | `cycling` `workout` |
| boating | Boating | `water_sports` `workout` |
| bootcamp | Bootcamp | `outdoor_recreation` `workout` |
| bouldering | Bouldering activity | `gym` `climbing` `workout` |
| bowling | Bowling | `individual_sports` `workout` |
| boxing | Boxing | `martial_arts` `workout` |
| calisthenics | Calisthenics | `gym` `workout` |
| canoeing | Canoeing | `water_sports` `workout` |
| capoeira | Capoeira | `martial_arts` `workout` |
| casual\_walking | Casual walking | `on_foot` `walking` `workout` |
| climbing | Climbing activity | `climbing` `workout` |
| cool\_down | Cool down | `workout` |
| core\_training | Core training | `gym` `workout` |
| cricket | Cricket | `team_sports` `workout` |
| cross\_country\_classic\_skiing | Cross-country classic skiing | `winter_sports` `workout` |
| cross\_country\_skate\_skiing | Cross-country skate skiing | `winter_sports` `workout` |
| crossfit | CrossFit | `gym` `workout` |
| curling | Curling | `winter_sports` `team_sports` `workout` |
| cycling | Cycling activity | `cycling` `workout` |
| cyclocross\_cycling | Cyclocross cycling activity | `cycling` `workout` |
| dancing | Dancing activity | `dancing` `workout` |
| disc | Disc sports | `team_sports` `workout` |
| diving | | `water_sports` `outdoor_recreation` `workout` |
| downhill\_biking | Downhill biking activity | `cycling` `workout` |
| duathlon | Duathlon activity | `running` `cycling` `workout` |
| e\_bike\_fitness | E-bike fitness activity | `cycling` `workout` |
| e\_bike\_mountain | E-bike mountain activity | `cycling` `workout` |
| ecg\_measurement | | `ecg` |
| elliptical | Elliptical training | `gym` `workout` |
| equestrian | Equestrian | `outdoor_recreation` `workout` |
| fencing | | `individual_sports` `martial_arts` `workout` |
| field\_hockey | Field hockey | `team_sports` `workout` |
| fishing | | `water_sports` `outdoor_recreation` `workout` |
| flexibility\_training | Flexibility training | `gym` `workout` |
| floorball | Floorball | `team_sports` `workout` |
| functional\_fitness | Functional fitness | `gym` `workout` |
| gardening | Gardening activity | `workout` |
| geocaching | Geocaching | `outdoor_recreation` `workout` |
| golf | Golf | `individual_sports` `workout` |
| gravel\_cycling | Gravel cycling activity | `cycling` `workout` |
| gym | Gym activity | `gym` `workout` |
| gymnastics | Gymnastics | `gym` `workout` |
| hand\_cycling | Hand cycling activity | `cycling` `workout` |
| handball | Handball | `team_sports` `workout` |
| hiit | High-intensity interval training | `gym` `workout` |
| hiking | Hiking | `on_foot` `walking` `workout` |
| horseriding | Horse riding | `outdoor_recreation` `workout` |
| house\_work | House work activity | `workout` |
| hunting | Hunting | `outdoor_recreation` `workout` |
| ice\_hockey | Ice hockey | `team_sports` `workout` |
| indoor\_cardio | Indoor cardio | `gym` `workout` |
| indoor\_climbing | Indoor climbing | `gym` `workout` |
| indoor\_cycling | Indoor cycling activity | `cycling` `gym` `workout` |
| indoor\_hand\_cycling | Indoor hand cycling activity | `cycling` `gym` `workout` |
| indoor\_rowing | Indoor rowing | `gym` `workout` |
| indoor\_running | Indoor running activity | `on_foot` `running` `gym` `workout` |
| jiu\_jitsu | Jiu Jitsu | `martial_arts` `workout` |
| judo | Judo | `martial_arts` `workout` |
| jumping | Jumping Rope | `individual_sports` `workout` |
| karate | Karate | `martial_arts` `workout` |
| kayaking | Kayaking | `water_sports` `workout` |
| kickboxing | Kickboxing | `martial_arts` `workout` |
| kiteboarding | Kiteboarding | `water_sports` `workout` |
| kung\_fu | Kung Fu | `martial_arts` `workout` |
| lacrosse | Lacrosse | `team_sports` `workout` |
| martial\_arts | Martial arts activity | `martial_arts` `workout` |
| meditation | Meditation | `gym` `workout` |
| mindfulness\_session | Mindfulness Session | `mindfulness` |
| mixed\_martial\_arts | Mixed martial arts | `martial_arts` `workout` |
| mixed\_training | Mixed training | `gym` `workout` |
| motorsports | Motorsports | `workout` |
| mountain\_biking | Mountain biking activity | `cycling` `workout` |
| muay\_thai | Muay Thai | `martial_arts` `workout` |
| multisport | Multisport | `workout` |
| netball | Netball | `team_sports` `workout` |
| obstacle\_run | Obstacle running activity | `on_foot` `running` `workout` |
| offshore\_grinding | Offshore grinding | `water_sports` `workout` |
| onshore\_grinding | Onshore grinding | `water_sports` `workout` |
| open\_water\_swimming | Open water swimming | `swimming` `workout` |
| orienteering | Orienteering | `outdoor_recreation` `workout` |
| other | Other activity | |
| outdoor\_recreation | Outdoor recreation activity | `outdoor_recreation` `workout` |
| paddle\_ball | Paddle ball | `racket_sports` `workout` |
| paddling | Paddling | `water_sports` `workout` |
| padel | Padel | `racket_sports` `workout` |
| paintball | Paintball | `team_sports` `workout` |
| paragliding | Paragliding | `outdoor_recreation` `workout` |
| parkour | Parkour | `individual_sports` `workout` |
| pickleball | Pickleball | `racket_sports` `workout` |
| pilates | Pilates | `gym` `workout` |
| platform\_tennis | Platform tennis | `racket_sports` `workout` |
| play | Play activity | `workout` |
| polo | Polo | `team_sports` `workout` |
| racket\_sports | Racket sports activity | `racket_sports` `workout` |
| racquetball | Racquetball | `racket_sports` `workout` |
| recumbent\_cycling | Recumbent cycling activity | `cycling` `workout` |
| road\_biking | Road biking activity | `cycling` `workout` |
| rollerblading | Rollerblading activity | `workout` |
| rope\_skipping | Rope skipping | `gym` `workout` |
| rowing | Rowing | `water_sports` `workout` |
| rugby | Rugby | `team_sports` `workout` |
| running | Running activity | `on_foot` `running` `workout` |
| sailing | Sailing | `water_sports` `workout` |
| sedentary | Sedentary activity | |
| skateboarding | Skateboarding | `workout` |
| skating | Skating activity | `workout` |
| skating\_skiing | Skating skiing | `winter_sports` `workout` |
| skiing | Skiing | `winter_sports` `workout` |
| skiing\_snowboarding | Skiing and snowboarding | `winter_sports` `workout` |
| sleep | | `sleep` |
| snorkeling | Snorkeling | `water_sports` `workout` |
| snow\_shoeing | Snow shoeing | `winter_sports` `workout` |
| snowboarding | Snowboarding | `winter_sports` `workout` |
| snowmobiling | Snowmobiling | `winter_sports` `workout` |
| soccer | Soccer | `team_sports` `workout` |
| softball | Softball | `team_sports` `workout` |
| speed\_walking | Speed walking | `on_foot` `walking` `workout` |
| squash | Squash | `racket_sports` `workout` |
| stair\_climbing | Stair climbing | `gym` `workout` |
| stand\_up\_paddleboarding | Stand-up paddleboarding | `water_sports` `workout` |
| street\_running | Street running activity | `on_foot` `running` `workout` |
| strength\_training | Strength training | `gym` `workout` |
| stretching | Stretching | `workout` |
| stroller | Stroller | `on_foot` `walking` `workout` |
| surfing | Surfing | `water_sports` `workout` |
| swimming | | `swimming` `workout` |
| table\_tennis | Table tennis | `racket_sports` `workout` |
| taekwondo | Taekwondo | `martial_arts` `workout` |
| tai\_chi | Tai Chi | `martial_arts` `workout` |
| team\_sports | Team sports activity | `team_sports` `workout` |
| tennis | Tennis | `racket_sports` `workout` |
| track\_cycling | Track cycling activity | `cycling` `workout` |
| track\_running | Track running activity | `on_foot` `running` `workout` |
| trail\_running | Trail running activity | `on_foot` `running` `workout` |
| treadmill\_running | Treadmill running activity | `on_foot` `running` `gym` `workout` |
| triathlon | Triathlon activity | `running` `cycling` `swimming` `workout` |
| ultra\_running | Ultra running activity | `on_foot` `running` `workout` |
| virtual\_ride | Virtual ride activity | `cycling` `workout` |
| virtual\_running | Virtual running activity | `on_foot` `running` `workout` |
| volleyball | Volleyball | `team_sports` `workout` |
| wakeboarding | Wakeboarding | `water_sports` `workout` |
| walking | Walking activity | `on_foot` `walking` `workout` |
| warm\_up | Warm-up | `gym` `workout` |
| water\_polo | Water polo | `water_sports` `team_sports` `workout` |
| water\_sports | Water sports activity | `water_sports` `workout` |
| waterskiing | Waterskiing | `water_sports` `workout` |
| weight\_lifting | Weight lifting | `gym` `workout` |
| wheelchair\_push\_run | Wheelchair push run | `wheelchair` `workout` |
| wheelchair\_push\_walk | Wheelchair push walk | `wheelchair` `workout` |
| whitewater\_rafting | Whitewater rafting | `water_sports` `workout` |
| windsurfing | Windsurfing | `water_sports` `workout` |
| winter\_sports | Winter sports activity | `winter_sports` `workout` |
| work | Working activity | `workout` |
| workout | General workout activity | `workout` |
| wrestling | Wrestling | `martial_arts` `workout` |
| yoga | Yoga | `gym` `workout` |
# Application Configuration
Source: https://docs.spikeapi.com/technical-references/application_configuration
Configure your Spike API application settings in the admin console
Configure your Spike API application through the [admin console](https://admin.spikeapi.com/) to control authentication, webhooks, data retention, and integration behavior.
Access the Spike API admin console to configure your application settings, manage integrations, and monitor your API usage.
## Application Identifiers
### Application ID
Your unique application identifier assigned when the application is created. Use this ID for API authentication and integration management.
## Authentication Keys
Access your application credentials from the admin console to authenticate and secure your API integration.
### HMAC Key
Secret key for HMAC-based authentication. Required for `hmac` auth flow applications. Must be at least 16 characters long.
For detailed implementation examples, see the [Authentication documentation](/api-docs/authentication).
### Webhook Signature Key
Secret key used to sign webhook payloads for security verification. Use this to validate that webhooks are genuinely from Spike API.
For implementation examples and signature verification code, see the [Webhooks documentation](/api-docs/webhooks).
## Integration Options
Configure your application settings through the admin console to customize integration behavior and data handling.
### Default Redirect URL
The URL users are redirected to after authenticating with a provider. Used when no specific redirect URL is provided in the OAuth2 flow.
**Placeholders available:**
* `{application_user_id}` - Your application's user identifier
* `{provider_slug}` - The connected provider (e.g., "garmin", "fitbit")
* `{provider_user_id}` - The user's ID from the provider
**Example:**
```
https://example.com/callback?user={application_user_id}&provider={provider_slug}
```
**Predefined parameters:** The system automatically appends these query parameters:
* `user_id={application_user_id}`
* `provider_slug={provider_slug}`
* `error={error_text}` (only when integration fails)
### Allowed Redirect Domains
List of domains permitted for redirect URLs to prevent redirect attacks. You can use `redirect_uri` as a placeholder for dynamic redirects.
**Example:**
```
https://example.com/callback?redirect_uri=http%3A%2F%2Fallowed-domain.com%2Fpath
```
### Webhook URLs
#### Main Webhook URL
Primary endpoint for receiving real-time data updates. Called whenever user health data changes.
**When triggered:**
* New health data from connected providers
* Data updates or corrections
* Provider integration status changes
#### Lab Reports Webhook URL
Dedicated endpoint for lab report processing completion notifications.
**When triggered:**
* Lab report analysis is completed
* Lab report processing fails
* New lab report data becomes available
#### Nutrition Records Webhook URL
Dedicated endpoint for nutrition data processing notifications.
**When triggered:**
* Nutrition record analysis is completed
* Nutrition data processing fails
* New nutrition data becomes available
### Data Backfill Configuration
#### Max Backfill (days)
Controls how far back the system attempts to fetch historical data when a user connects a new provider.
**Limitations:**
* Maximum 90 days
* Cannot exceed your Dataset Retention Policy setting
* Provider-specific limitations may apply (e.g., some providers only provide 30 days)
**Provider examples:**
* Fitbit: Up to 90 days
* Garmin: Up to 90 days
* Oura: Up to 180 days (recommended max 30 days)
* Apple HealthKit: Limited by user's device data
* Android Health Connect: 30 days + optional PERMISSION\_READ\_HEALTH\_DATA\_HISTORY
For complete provider-specific limitations and capabilities, see the [Provider Matrix](/technical-references/provider_matrix).
For SDK-based providers (iOS HealthKit, Android Health Connect), manual data extraction is required. See the [SDK backfill documentation](/sdk-docs/ios/backfill) for implementation details.
## Best Practices
### Security
* Keep authentication keys secure and rotate them regularly
* Use HTTPS for all webhook and redirect URLs
* Validate webhook signatures using your Webhook Signature Key
* Restrict allowed redirect domains to prevent attacks
### Performance
* Use dedicated webhook URLs for different data types when you need specialized handling
* Set appropriate backfill limits based on your processing capacity
* Consider rate limiting on your webhook endpoints
### Monitoring
* Monitor webhook delivery success rates
* Set up alerts for authentication failures
* Track backfill completion status for new integrations
### Development vs Production
* Use different applications for development and production environments
* Test webhook endpoints thoroughly before going live
* Start with shorter backfill periods during development
# Backfill of Historical Data
Source: https://docs.spikeapi.com/technical-references/backfill
Comprehensive guide to backfill mechanisms for retrieving historical health data across different providers and platforms.
## About
After successfully creating a Spike API integration, a data stream of wearables recorded data becomes available for the application user when making API calls and also through webhooks. Usually only data recorded from the moment of authorization is shared, but Spike has developed mechanisms to fetch data recorded retrospectively as well. This document describes the feature called **Backfill** and principles of how to use it, as well as the limitations.
## Configuration
In the admin console, under Application configuration you can find the setting called **"Max Backfill (days)"**. By default it's disabled (set to nothing or 0). To enable the backfill mechanism you need to set the value to the required number of days and save it.
## Core Principles
Once backfill is enabled and new integration gets created, here's what you should expect:
* **Timing**: Backfill is triggered post new integration creation. Settings would have no effect on integrations that are already created and have the same data accumulated by natural lifecycle
* **Data Delivery**: Data delivery (API and webhooks) is unified, but backfilling process is unique to each provider
* **Asynchronous Processing**: Backfilling mostly is an asynchronous process. Seeking the best user experience for application users, they will see integration being successfully created (redirected back to configured postback URL) and the process might continue in the background
## Implementation Categories
### Manual Implementation Required
Some providers require manual data extraction through SDK calls:
#### Apple HealthKit
Data is stored exclusively on the device (iPhone). Manual implementation is required because Apple doesn't offer any other communication except the HealthKit framework.
**Limitations:**
* What data the user has on their device
* What permissions your app has been granted (read access for specific data types)
* User's health app settings or deletions
* Your retention policy settings
**Implementation:** See [iOS SDK Backfill Guide](/sdk-docs/ios/backfill) for detailed implementation instructions.
#### Google Health Connect
Data is stored exclusively on the device (running Android). Manual implementation is required because Google doesn't offer any other communication except the Health Connect client.
**Limitations:**
* What data the user has on their device
* What permissions your app has been granted (read access for specific data types)
* User's health app settings or deletions
* Your retention policy settings
* 30 days period predating permission grant
**Special Considerations:**
* Permission request timing is not relevant - if an application user decides to approve permission a week later by going directly into Health Connect settings menu, you will be able to access data 23 days prior to installing the app
* For apps targeting Android API level 34 (Android 14) and higher, Google has introduced `PERMISSION_READ_HEALTH_DATA_HISTORY` which allows access to health data recorded before the app was installed
**Implementation:** See [Android SDK Backfill Guide](/sdk-docs/android/backfill) for detailed implementation instructions.
### Automatic Backfill
Most third-party providers handle backfill automatically with no additional effort required from your side. Each provider has different availability windows and processing times.
For complete details on each provider's backfill capabilities, availability windows, and specific limitations, refer to the [Supported Providers](/technical-references/provider_matrix) documentation.
## Best Practices
### For Manual Implementation (iOS/Android)
* Keep the backfill process asynchronous for optimal user experience
* Segment requests into smaller date ranges to ensure optimal performance
* Implement rate limiting to avoid overwhelming the health data store
* Handle permissions gracefully - users may grant partial access
* Provide meaningful feedback about data access issues
### For Automatic Backfill
* Listen for incoming webhooks to know when data becomes available
* Be patient with the asynchronous process - some providers may take hours
* Understand that data may not arrive in chronological order
* Consider the varying limitations across providers when setting expectations
### General Recommendations
* Set realistic backfill periods considering provider limitations
* Implement robust error handling for various scenarios
* Respect user privacy and data access permissions
* Design your application to handle partial or delayed data availability
## Provider-Specific Details
For complete details on each provider's capabilities, limitations, and backfill availability, refer to the [Supported Providers](/technical-references/provider_matrix) documentation.
For platform-specific implementation guides:
* [iOS SDK Backfill Guide](/sdk-docs/ios/backfill)
* [Android SDK Backfill Guide](/sdk-docs/android/backfill)
## Monitoring and Troubleshooting
To effectively monitor backfill operations:
1. **Webhook Monitoring**: Set up webhook listeners to track when historical data becomes available
2. **Error Handling**: Implement comprehensive error handling for permission issues, data unavailability, and provider limitations
3. **User Communication**: Provide clear feedback to users about the backfill process and expected timelines
4. **Logging**: Maintain detailed logs of backfill requests and outcomes for debugging purposes
# Metric Types and Units
Source: https://docs.spikeapi.com/technical-references/metrics_matrix
This document provides a comprehensive overview of all metrics, detailing their providers, descriptions, units, and number of decimals for output precision. The table below summarizes the key features supported by each metric.
| **Metric** | **Providers** | **Description** | **Unit** | **Precision** |
| :---------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------- | :---------- | :------------ |
| ascent | `garmin` `polar` `suunto` `huawei` `coros` | Total ascent | meters | 2 |
| bedtime\_duration | `fitbit` `oura` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Bedtime duration | ms | 0 |
| blood\_pressure\_diastolic | `apple` `health_connect` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` | Diastolic blood pressure | mmHg | 2 |
| blood\_pressure\_diastolic\_max | `apple` `health_connect` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` | Maximum diastolic blood pressure | mmHg | 2 |
| blood\_pressure\_diastolic\_min | `apple` `health_connect` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` | Minimum diastolic blood pressure | mmHg | 2 |
| blood\_pressure\_systolic | `apple` `health_connect` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` | Systolic blood pressure | mmHg | 2 |
| blood\_pressure\_systolic\_max | `apple` `health_connect` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` | Maximum systolic blood pressure | mmHg | 2 |
| blood\_pressure\_systolic\_min | `apple` `health_connect` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` | Minimum systolic blood pressure | mmHg | 2 |
| body\_bone\_mass | `garmin` `withings` | Bone mass | g | 0 |
| body\_fat | `apple` `health_connect` `fitbit` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Body fat percentage | percentage | 2 |
| body\_fat\_max | `apple` `health_connect` `fitbit` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Maximum body fat percentage | percentage | 2 |
| body\_fat\_min | `apple` `health_connect` `fitbit` `garmin` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Minimum body fat percentage | percentage | 2 |
| body\_mass\_index | `apple` `health_connect` `fitbit` `garmin` `samsung_health_data` `omron_us` `omron_eu` | Body mass index | count | 4 |
| body\_temperature | `apple` `health_connect` `fitbit` `oura` `polar` `withings` `samsung_health_data` `omron_us` `omron_eu` | Body temperature | celsius | 2 |
| body\_temperature\_max | `apple` `health_connect` `fitbit` `oura` `polar` `withings` `samsung_health_data` `omron_us` `omron_eu` | Maximum body temperature | celsius | 2 |
| body\_temperature\_min | `apple` `health_connect` `fitbit` `oura` `polar` `withings` `samsung_health_data` `omron_us` `omron_eu` | Minimum body temperature | celsius | 2 |
| breathing\_rate | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `whoop` `withings` `google_health` | Breathing rate | breaths/min | 4 |
| breathing\_rate\_max | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `whoop` `withings` `google_health` | Maximum breathing rate | breaths/min | 4 |
| breathing\_rate\_min | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `whoop` `withings` `google_health` | Minimum breathing rate | breaths/min | 4 |
| cadence | `garmin` `polar` `strava` `suunto` `huawei` `coros` `samsung_health_data` | Average cadence | rpm | 4 |
| cadence\_max | `garmin` `polar` `strava` `suunto` `huawei` `coros` `samsung_health_data` | Maximum cadence | rpm | 4 |
| cadence\_min | `garmin` `polar` `strava` `suunto` `huawei` `coros` `samsung_health_data` | Minimum cadence | rpm | 4 |
| calories\_burned | `apple` `health_connect` `fitbit` `oura` `strava` `suunto` `whoop` `huawei` `coros` `samsung_health_data` `google_health` | Total calories burned | kcal | 4 |
| calories\_burned\_active | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `coros` `samsung_health_data` | Active calories burned | kcal | 4 |
| calories\_burned\_basal | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `withings` | Basal calories burned | kcal | 4 |
| calories\_intake | `huawei` | Calories intake | kcal | 4 |
| descent | `garmin` `polar` `suunto` `huawei` `coros` | Total descent | meters | 2 |
| distance | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `strava` `suunto` `whoop` `withings` `huawei` `coros` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Total distance | meters | 4 |
| distance\_cycling | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `strava` `suunto` `whoop` `coros` `samsung_health_data` | Cycling distance | meters | 4 |
| distance\_running | `garmin` `oura` `polar` `strava` `suunto` `whoop` `coros` `samsung_health_data` | Running distance | meters | 4 |
| distance\_swimming | `apple` `health_connect` `fitbit` `garmin` `polar` `strava` `whoop` `coros` `samsung_health_data` | Swimming distance | meters | 4 |
| distance\_walking | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `strava` `suunto` `whoop` `coros` `samsung_health_data` | Walking distance | meters | 4 |
| distance\_wheelchair | `apple` `health_connect` `fitbit` `garmin` `polar` `strava` `whoop` | Wheelchair distance | meters | 4 |
| duration\_active | `fitbit` `garmin` `polar` `suunto` `huawei` `samsung_health_data` | Active duration | ms | 0 |
| duration\_high\_intensity | `fitbit` `garmin` `oura` `withings` | Duration of high intensity | ms | 0 |
| duration\_low\_intensity | `fitbit` `oura` `withings` | Duration of low intensity | ms | 0 |
| duration\_moderate\_intensity | `fitbit` `garmin` `oura` `withings` `huawei` | Duration of moderate intensity | ms | 0 |
| ecg\_rri | `polar` | ECG R-R interval | ms | 0 |
| ecg\_voltage | `apple` `fitbit` `polar` `withings` `huawei` | ECG voltage | uV | 2 |
| elevation | `apple` `fitbit` `garmin` `polar` `suunto` `withings` `huawei` `coros` `samsung_health_data` `google_health` | Elevation | meters | 2 |
| elevation\_gain | `health_connect` `polar` `strava` `whoop` `huawei` `google_health` | Total elevation gain | meters | 2 |
| elevation\_loss | `polar` `whoop` `huawei` | Total elevation loss | meters | 2 |
| elevation\_max | `apple` `fitbit` `garmin` `polar` `strava` `suunto` `withings` `huawei` `coros` `samsung_health_data` `google_health` | Maximum elevation | meters | 2 |
| elevation\_min | `apple` `fitbit` `garmin` `polar` `strava` `suunto` `withings` `huawei` `coros` `samsung_health_data` `google_health` | Minimum elevation | meters | 2 |
| floors\_climbed | `apple` `health_connect` `fitbit` `garmin` `google_health` | Floors climbed | count | 0 |
| glucose | `apple` `health_connect` `ultrahuman` `samsung_health_data` `dexcom` `freestyle_libre` `google_health` | Glucose | mg/dL | 6 |
| heartrate | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `strava` `suunto` `whoop` `withings` `huawei` `ultrahuman` `coros` `samsung_health_data` `luna` `omron_us` `omron_eu` `google_health` | Heart rate | bpm | 0 |
| heartrate\_max | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `strava` `suunto` `whoop` `withings` `huawei` `ultrahuman` `coros` `samsung_health_data` `luna` `omron_us` `omron_eu` `google_health` | Maximum heart rate | bpm | 0 |
| heartrate\_min | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `strava` `suunto` `whoop` `withings` `huawei` `ultrahuman` `coros` `samsung_health_data` `luna` `omron_us` `omron_eu` `google_health` | Minimum heart rate | bpm | 0 |
| heartrate\_resting | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `whoop` `huawei` `ultrahuman` `coros` `google_health` | Resting heart rate | bpm | 0 |
| height | `apple` `health_connect` `oura` `polar` `whoop` `withings` `huawei` `samsung_health_data` `google_health` | Height | meters | 4 |
| hrv\_rmssd | `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `ultrahuman` `coros` `luna` `google_health` | Heart rate variability (RMSSD) | ms | 0 |
| hrv\_sdnn | `apple` `withings` `google_health` | Heart rate variability (SDNN) | ms | 0 |
| latitude | `apple` `fitbit` `garmin` `polar` `suunto` `samsung_health_data` `google_health` | Latitude | degrees | 8 |
| longitude | `apple` `fitbit` `garmin` `polar` `suunto` `samsung_health_data` `google_health` | Longitude | degrees | 8 |
| pace | `garmin` `suunto` `huawei` | Average pace | sec/m | 2 |
| power | `samsung_health_data` | Power | w | 0 |
| power\_max | `samsung_health_data` | Maximum power | w | 0 |
| skin\_temperature | `apple` `health_connect` `polar` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Skin temperature | celsius | 2 |
| skin\_temperature\_max | `apple` `health_connect` `polar` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Maximum skin temperature | celsius | 2 |
| skin\_temperature\_min | `apple` `health_connect` `polar` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Minimum skin temperature | celsius | 2 |
| sleep\_duration | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `coros` `samsung_health_data` `luna` `google_health` | Total sleep duration | ms | 0 |
| sleep\_duration\_awake | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Awake duration during sleep | ms | 0 |
| sleep\_duration\_deep | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Deep sleep duration | ms | 0 |
| sleep\_duration\_light | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | Light sleep duration | ms | 0 |
| sleep\_duration\_nap | `fitbit` `garmin` `oura` `suunto` `whoop` `luna` | Nap duration | ms | 0 |
| sleep\_duration\_rem | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `luna` `google_health` | REM sleep duration | ms | 0 |
| sleep\_efficiency | `oura` `polar` `whoop` `withings` `huawei` `ultrahuman` | Sleep efficiency | percentage | 0 |
| sleep\_interruptions | `health_connect` `fitbit` `garmin` `oura` `whoop` `withings` `huawei` `samsung_health_data` `luna` `google_health` | Sleep interruptions | count | 0 |
| sleep\_latency | `fitbit` `oura` `suunto` `withings` `huawei` `samsung_health_data` `luna` | Sleep latency | ms | 0 |
| sleep\_skin\_temperature\_deviation | `fitbit` `garmin` `oura` `polar` | Skin temperature deviation from the baseline | celsius | 2 |
| speed | `apple` `health_connect` `fitbit` `garmin` `polar` `strava` `suunto` `huawei` `coros` `samsung_health_data` | Average speed | m/sec | 4 |
| speed\_max | `apple` `health_connect` `fitbit` `garmin` `polar` `strava` `suunto` `huawei` `coros` `samsung_health_data` | Maximum speed | m/sec | 4 |
| speed\_min | `apple` `health_connect` `fitbit` `garmin` `polar` `strava` `suunto` `huawei` `coros` `samsung_health_data` | Minimum speed | m/sec | 4 |
| spo2 | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Blood oxygen level | percentage | 4 |
| spo2\_max | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Maximum blood oxygen level | percentage | 4 |
| spo2\_min | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `whoop` `withings` `huawei` `ultrahuman` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Minimum blood oxygen level | percentage | 4 |
| steps | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `suunto` `withings` `huawei` `ultrahuman` `coros` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Total steps | count | 0 |
| swimming\_distance\_per\_stroke | `garmin` `polar` `suunto` `coros` `samsung_health_data` | Swimming distance per stroke | meters | 2 |
| swimming\_lengths | `garmin` `polar` `suunto` `coros` `samsung_health_data` | Swimming lengths | count | 0 |
| vo2max | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `withings` `ultrahuman` `google_health` | VO2 max | mL/kg/min | 2 |
| wakeup\_latency | `fitbit` `suunto` `samsung_health_data` | Wakeup latency | ms | 0 |
| weight | `apple` `health_connect` `fitbit` `garmin` `oura` `polar` `whoop` `withings` `huawei` `samsung_health_data` `omron_us` `omron_eu` `google_health` | Weight | g | 4 |
# Nutritional Fields
Source: https://docs.spikeapi.com/technical-references/nutritional_fields
The API supports analysis of 29 comprehensive nutritional fields, organized into macronutrients and micronutrients.
### Basic Macronutrients
Always included unless specifically excluded:
* **Energy** (`energy_kcal`) in kilocalories
* **Carbohydrates** (`carbohydrate_g`) in grams
* **Protein** (`protein_g`) in grams
* **Total Fat** (`fat_total_g`) in grams
### Fat Breakdown
* **Saturated Fat** (`fat_saturated_g`) in grams
* **Polyunsaturated Fat** (`fat_polyunsaturated_g`) in grams (micronutrient)
* **Monounsaturated Fat** (`fat_monounsaturated_g`) in grams (micronutrient)
* **Trans Fat** (`fat_trans_g`) in grams
### Additional Macronutrient Details
* **Total Sugars** (`sugars_total_g`) in grams
* **Dietary Fiber** (`fiber_total_dietary_g`) in grams (micronutrient)
* **Cholesterol** (`cholesterol_mg`) in milligrams (micronutrient)
### Essential Minerals
* **Sodium** (`sodium_mg`) in milligrams
* **Potassium** (`potassium_mg`) in milligrams
* **Calcium** (`calcium_mg`) in milligrams
* **Iron** (`iron_mg`) in milligrams
* **Magnesium** (`magnesium_mg`) in milligrams
* **Phosphorus** (`phosphorus_mg`) in milligrams
* **Zinc** (`zinc_mg`) in milligrams
### Vitamins
* **Vitamin A** (`vitamin_a_rae_mcg`) in micrograms RAE
* **Vitamin C** (`vitamin_c_mg`) in milligrams
* **Vitamin D** (`vitamin_d_mcg`) in micrograms
* **Vitamin E** (`vitamin_e_mg`) in milligrams
* **Vitamin K** (`vitamin_k_mcg`) in micrograms
### B-Complex Vitamins
* **Thiamin (B1)** (`thiamin_mg`) in milligrams
* **Riboflavin (B2)** (`riboflavin_mg`) in milligrams
* **Niacin (B3)** (`niacin_mg`) in milligrams
* **Vitamin B6** (`vitamin_b6_mg`) in milligrams
* **Folate** (`folate_mcg`) in micrograms
* **Vitamin B12** (`vitamin_b12_mcg`) in micrograms
# Supported Providers
Source: https://docs.spikeapi.com/technical-references/provider_matrix
This document provides a comprehensive overview of all providers, detailing their capabilities. The list below summarizes the key features supported by each provider.
## Apple HealthKit
**Slug: `apple`**
### Intraday Metrics
`blood_pressure_diastolic` `blood_pressure_diastolic_max` `blood_pressure_diastolic_min` `blood_pressure_systolic` `blood_pressure_systolic_max` `blood_pressure_systolic_min` `body_fat` `body_fat_max` `body_fat_min` `body_mass_index` `body_temperature` `body_temperature_max` `body_temperature_min` `breathing_rate` `breathing_rate_max` `breathing_rate_min` `calories_burned` `calories_burned_active` `calories_burned_basal` `distance` `distance_cycling` `distance_swimming` `distance_walking` `distance_wheelchair` `floors_climbed` `glucose` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `hrv_sdnn` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `speed` `speed_max` `speed_min` `spo2` `spo2_max` `spo2_min` `steps` `vo2max` `weight` `ecg_voltage`
### Daily Metrics
`calories_burned` `calories_burned_active` `calories_burned_basal` `distance` `distance_cycling` `distance_wheelchair` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `hrv_sdnn` `steps`
### Sleep Metrics
`breathing_rate` `breathing_rate_max` `breathing_rate_min` `heartrate` `heartrate_max` `heartrate_min` `hrv_sdnn` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `spo2` `spo2_max` `spo2_min`
### Workout Metrics
`calories_burned` `calories_burned_active` `calories_burned_basal` `distance` `distance_cycling` `distance_swimming` `distance_walking` `distance_wheelchair` `elevation` `elevation_max` `elevation_min` `floors_climbed` `heartrate` `heartrate_max` `heartrate_min` `hrv_sdnn` `latitude` `longitude` `speed` `speed_max` `speed_min` `spo2` `spo2_max` `spo2_min` `steps` `vo2max`
### User Properties
`body_fat` `body_mass_index` `height` `weight` `body_fat_min` `body_fat_max` `birth_date` `gender`
### Backfill Limitations
Manual implementation required. Limited by device data, permissions, and user settings. Refer to iOS SDK documentation.
***
## Coros
**Slug: `coros`**
### Daily Metrics
`calories_burned` `heartrate_resting` `steps`
### Sleep Metrics
`heartrate` `hrv_rmssd` `sleep_duration` `heartrate_max` `heartrate_min`
### Workout Metrics
`calories_burned` `distance` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `heartrate` `speed` `steps` `heartrate_max` `heartrate_min` `speed_max` `speed_min` `ascent` `cadence` `cadence_max` `cadence_min` `calories_burned_active` `descent` `elevation` `elevation_max` `elevation_min` `swimming_distance_per_stroke` `swimming_lengths`
***
## Dexcom
**Slug: `dexcom`**
### Intraday Metrics
`glucose`
***
## Fitbit
**Slug: `fitbit`**
### Intraday Metrics
`heartrate` `heartrate_max` `heartrate_min` `steps` `distance` `calories_burned` `calories_burned_basal` `hrv_rmssd` `spo2` `spo2_min` `spo2_max` `body_temperature` `body_temperature_max` `body_temperature_min` `ecg_voltage`
### Daily Metrics
`calories_burned` `calories_burned_active` `calories_burned_basal` `duration_active` `duration_low_intensity` `duration_moderate_intensity` `duration_high_intensity` `floors_climbed` `heartrate_resting` `steps` `hrv_rmssd` `vo2max` `breathing_rate` `breathing_rate_min` `breathing_rate_max` `sleep_skin_temperature_deviation`
### Sleep Metrics
`bedtime_duration` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_nap` `sleep_duration_rem` `sleep_interruptions` `sleep_latency` `wakeup_latency`
### Workout Metrics
`calories_burned_active` `distance` `distance_cycling` `distance_swimming` `distance_walking` `distance_wheelchair` `elevation` `elevation_max` `elevation_min` `heartrate` `latitude` `longitude` `speed` `steps` `heartrate_max` `heartrate_min` `speed_max` `speed_min`
### User Properties
`body_fat` `body_fat_min` `body_fat_max` `body_mass_index` `weight`
### Backfill Limitations
Up to 90 days available. Activities and heart rate time series limited to 30 days. Automatic backfill may take minutes to hours.
***
## Freestyle Libre
**Slug: `freestyle_libre`**
### Intraday Metrics
`glucose`
***
## Garmin
**Slug: `garmin`**
### Intraday Metrics
`calories_burned_active` `distance` `steps` `blood_pressure_systolic` `blood_pressure_diastolic` `heartrate` `heartrate_max` `heartrate_min` `blood_pressure_systolic_min` `blood_pressure_systolic_max` `blood_pressure_diastolic_min` `blood_pressure_diastolic_max`
### Daily Metrics
`calories_burned_active` `calories_burned_basal` `distance` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `distance_wheelchair` `duration_active` `duration_moderate_intensity` `duration_high_intensity` `floors_climbed` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `steps` `hrv_rmssd` `vo2max` `sleep_skin_temperature_deviation`
### Sleep Metrics
`breathing_rate` `breathing_rate_max` `breathing_rate_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_nap` `sleep_duration_rem` `sleep_interruptions` `spo2` `spo2_max` `spo2_min`
### Workout Metrics
`ascent` `cadence` `cadence_max` `calories_burned_active` `descent` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `distance_wheelchair` `heartrate` `heartrate_max` `latitude` `longitude` `pace` `speed` `speed_max` `steps` `swimming_lengths` `heartrate_min` `speed_min` `cadence_min` `distance` `duration_active` `elevation` `elevation_max` `elevation_min` `floors_climbed` `floors_climbed_max` `spo2` `spo2_max` `spo2_min` `swimming_distance_per_stroke`
### User Properties
`body_mass_index` `weight` `body_fat` `body_bone_mass` `body_fat_min` `body_fat_max`
### Backfill Limitations
Up to 90 days available. One backfill per user per application. May take up to 12 hours.
***
## Google Health (beta)
**Slug: `google_health`**
### Intraday Metrics
`distance` `floors_climbed` `steps` `body_fat` `glucose` `heartrate` `hrv_rmssd` `hrv_sdnn` `spo2` `vo2max` `weight` `heartrate_max` `heartrate_min` `spo2_max` `spo2_min` `body_fat_min` `body_fat_max`
### Daily Metrics
`breathing_rate` `heartrate_resting` `skin_temperature` `spo2` `spo2_max` `spo2_min` `breathing_rate_min` `breathing_rate_max` `skin_temperature_max` `skin_temperature_min`
### Sleep Metrics
`bedtime_duration` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_interruptions`
### Workout Metrics
`calories_burned` `distance` `elevation_gain` `heartrate` `steps` `heartrate_max` `heartrate_min` `elevation` `latitude` `longitude` `elevation_max` `elevation_min`
### User Properties
`body_fat` `height` `weight` `body_fat_min` `body_fat_max`
***
## Google Health Connect
**Slug: `health_connect`**
### Intraday Metrics
`blood_pressure_diastolic` `blood_pressure_diastolic_max` `blood_pressure_diastolic_min` `blood_pressure_systolic` `blood_pressure_systolic_max` `blood_pressure_systolic_min` `body_temperature` `body_temperature_max` `body_temperature_min` `breathing_rate` `breathing_rate_max` `breathing_rate_min` `calories_burned` `calories_burned_active` `calories_burned_basal` `distance` `elevation_gain` `floors_climbed` `glucose` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `hrv_rmssd` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `spo2` `spo2_max` `spo2_min` `speed` `speed_max` `speed_min` `steps` `vo2max` `weight`
### Daily Metrics
`calories_burned` `calories_burned_active` `calories_burned_basal` `distance` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `steps`
### Sleep Metrics
`heartrate` `heartrate_max` `heartrate_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_interruptions`
### Workout Metrics
`calories_burned` `calories_burned_active` `calories_burned_basal` `distance` `distance_cycling` `distance_swimming` `distance_walking` `distance_wheelchair` `heartrate` `heartrate_max` `heartrate_min` `spo2` `spo2_max` `spo2_min` `speed` `speed_max` `speed_min` `steps` `vo2max`
### User Properties
`body_fat` `body_mass_index` `bone_mass` `height` `weight` `body_fat_min` `body_fat_max`
### Backfill Limitations
Manual implementation required. Limited by device data and permissions. Up to 30 days predating permission grant. Refer to Android SDK documentation.
***
## Huawei
**Slug: `huawei`**
### Intraday Metrics
`blood_pressure_diastolic` `blood_pressure_systolic` `calories_burned` `distance` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `skin_temperature` `spo2` `steps` `spo2_max` `spo2_min` `skin_temperature_max` `skin_temperature_min` `blood_pressure_systolic_min` `blood_pressure_systolic_max` `blood_pressure_diastolic_min` `blood_pressure_diastolic_max` `ecg_voltage` `speed` `speed_max` `speed_min`
### Daily Metrics
`duration_active` `steps` `calories_intake` `distance` `duration_moderate_intensity`
### Sleep Metrics
`bedtime_duration` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_efficiency` `sleep_interruptions` `sleep_latency`
### Workout Metrics
`ascent` `cadence` `cadence_max` `cadence_min` `calories_intake` `descent` `distance` `duration_active` `elevation` `elevation_gain` `elevation_loss` `elevation_max` `elevation_min` `heartrate` `heartrate_max` `heartrate_min` `pace` `speed` `speed_max` `speed_min` `steps`
### User Properties
`weight` `body_fat` `body_fat_min` `body_fat_max` `height`
***
## Luna
**Slug: `luna`**
### Intraday Metrics
`heartrate` `hrv_rmssd` `heartrate_max` `heartrate_min`
### Daily Metrics
`heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd`
### Sleep Metrics
`bedtime_duration` `skin_temperature` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_nap` `sleep_duration_rem` `sleep_interruptions` `sleep_latency` `skin_temperature_max` `skin_temperature_min`
***
## Omron EU
**Slug: `omron_eu`**
### Intraday Metrics
`blood_pressure_diastolic` `blood_pressure_systolic` `heartrate` `heartrate_max` `heartrate_min` `blood_pressure_systolic_min` `blood_pressure_systolic_max` `blood_pressure_diastolic_min` `blood_pressure_diastolic_max` `calories` `distance` `steps` `body_fat` `body_mass_index` `weight` `body_fat_min` `body_fat_max` `body_temperature` `body_temperature_max` `body_temperature_min` `spo2` `spo2_max` `spo2_min`
### User Properties
`body_fat` `body_mass_index` `weight` `body_fat_min` `body_fat_max`
***
## Omron US
**Slug: `omron_us`**
### Intraday Metrics
`blood_pressure_diastolic` `blood_pressure_systolic` `heartrate` `heartrate_max` `heartrate_min` `blood_pressure_systolic_min` `blood_pressure_systolic_max` `blood_pressure_diastolic_min` `blood_pressure_diastolic_max` `calories` `distance` `steps` `body_fat` `body_mass_index` `weight` `body_fat_min` `body_fat_max` `body_temperature` `body_temperature_max` `body_temperature_min` `spo2` `spo2_max` `spo2_min`
### User Properties
`body_fat` `body_mass_index` `weight` `body_fat_min` `body_fat_max`
***
## Oura
**Slug: `oura`**
### Intraday Metrics
`heartrate` `heartrate_max` `heartrate_min`
### Daily Metrics
`calories_burned` `calories_burned_active` `calories_burned_basal` `duration_low_intensity` `duration_moderate_intensity` `duration_high_intensity` `steps` `spo2` `spo2_max` `spo2_min` `vo2max`
### Sleep Metrics
`bedtime_duration` `body_temperature` `breathing_rate` `heartrate` `heartrate_min` `heartrate_resting` `hrv_rmssd` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_nap` `sleep_duration_rem` `sleep_efficiency` `sleep_interruptions` `sleep_latency` `sleep_skin_temperature_deviation` `heartrate_max` `breathing_rate_min` `breathing_rate_max` `body_temperature_max` `body_temperature_min`
### Workout Metrics
`calories_burned` `distance` `distance_cycling` `distance_running` `distance_walking` `heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd`
### User Properties
`height` `weight` `gender` `age`
### Backfill Limitations
Up to 180 days available. Heart rate data limited to 30 days. Recommend 30 days or less.
***
## Polar
**Slug: `polar`**
### Intraday Metrics
`heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd` `spo2` `spo2_max` `spo2_min` `body_temperature` `body_temperature_max` `body_temperature_min` `height` `heartrate_resting` `vo2max` `ecg_rri` `ecg_voltage`
### Daily Metrics
`calories_burned_active` `calories_burned_basal` `steps` `breathing_rate` `breathing_rate_max` `breathing_rate_min` `heartrate` `hrv_rmssd` `heartrate_max` `heartrate_min` `skin_temperature` `sleep_skin_temperature_deviation` `skin_temperature_max` `skin_temperature_min` `vo2max` `heartrate_resting`
### Sleep Metrics
`heartrate` `heartrate_max` `heartrate_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_efficiency`
### Workout Metrics
`ascent` `calories_burned_active` `descent` `distance` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `distance_wheelchair` `elevation` `elevation_gain` `elevation_loss` `elevation_max` `elevation_min` `heartrate` `heartrate_max` `heartrate_min` `speed` `speed_max` `speed_min` `cadence` `cadence_max` `cadence_min` `duration_active` `latitude` `longitude` `steps` `swimming_distance_per_stroke` `swimming_lengths`
### User Properties
`height` `weight`
### Backfill Limitations
Up to 28 days available.
***
## Samsung Health Data
**Slug: `samsung_health_data`**
### Intraday Metrics
`calories_burned` `calories_burned_active` `distance` `glucose` `steps` `blood_pressure_diastolic` `blood_pressure_systolic` `body_temperature` `heartrate` `heartrate_max` `heartrate_min` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `spo2` `spo2_max` `spo2_min` `body_temperature_max` `body_temperature_min` `blood_pressure_systolic_min` `blood_pressure_systolic_max` `blood_pressure_diastolic_min` `blood_pressure_diastolic_max`
### Sleep Metrics
`bedtime_duration` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_interruptions` `sleep_latency` `wakeup_latency`
### Workout Metrics
`cadence` `cadence_max` `cadence_min` `calories_burned` `calories_burned_active` `distance` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `duration_active` `elevation` `elevation_max` `elevation_min` `heartrate` `heartrate_max` `heartrate_min` `latitude` `longitude` `power` `power_max` `speed` `speed_max` `speed_min` `spo2` `spo2_max` `spo2_min` `steps` `swimming_distance_per_stroke` `swimming_lengths`
### User Properties
`body_fat` `body_mass_index` `height` `weight` `body_fat_min` `body_fat_max` `birth_date` `birth_year` `gender`
***
## Strava
**Slug: `strava`**
### Workout Metrics
`cadence` `calories_burned` `distance` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `distance_wheelchair` `elevation_gain` `elevation_max` `elevation_min` `heartrate` `heartrate_max` `speed` `speed_max` `heartrate_min` `speed_min` `cadence_min` `cadence_max`
***
## Suunto
**Slug: `suunto`**
### Intraday Metrics
`calories_burned_active` `elevation` `heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd` `spo2` `steps` `elevation_max` `elevation_min` `spo2_max` `spo2_min`
### Daily Metrics
`calories_burned_active` `steps`
### Sleep Metrics
`heartrate` `heartrate_min` `hrv_rmssd` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_nap` `sleep_duration_rem` `sleep_latency` `spo2` `wakeup_latency` `heartrate_max` `spo2_max` `spo2_min`
### Workout Metrics
`ascent` `cadence` `cadence_max` `cadence_min` `calories_burned` `descent` `distance` `elevation_max` `elevation_min` `heartrate` `heartrate_max` `latitude` `longitude` `pace` `speed` `speed_max` `steps` `heartrate_min` `speed_min` `calories_burned_active` `distance_cycling` `distance_running` `distance_walking` `duration_active` `elevation` `swimming_distance_per_stroke` `swimming_lengths`
### Backfill Limitations
Up to 28 days available.
***
## Ultrahuman
**Slug: `ultrahuman`**
### Intraday Metrics
`heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `steps` `glucose`
### Daily Metrics
`heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd` `skin_temperature` `skin_temperature_min` `skin_temperature_max` `heartrate_resting` `steps` `vo2max`
### Sleep Metrics
`bedtime_duration` `heartrate` `heartrate_max` `heartrate_min` `hrv_rmssd` `skin_temperature` `skin_temperature_max` `skin_temperature_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_efficiency` `spo2` `spo2_max` `spo2_min`
***
## Whoop
**Slug: `whoop`**
### Daily Metrics
`calories_burned` `heartrate` `heartrate_max` `heartrate_min` `heartrate_resting` `hrv_rmssd` `skin_temperature` `spo2` `spo2_max` `spo2_min` `skin_temperature_max` `skin_temperature_min`
### Sleep Metrics
`breathing_rate` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_nap` `sleep_duration_rem` `sleep_efficiency` `sleep_interruptions` `breathing_rate_min` `breathing_rate_max`
### Workout Metrics
`calories_burned_active` `distance` `distance_cycling` `distance_running` `distance_swimming` `distance_walking` `distance_wheelchair` `elevation_gain` `elevation_loss` `heartrate` `heartrate_max` `heartrate_min`
### User Properties
`height` `weight`
### Backfill Limitations
Up to 180 days available. Recommend 30 days or less for consistency.
***
## Withings
**Slug: `withings`**
### Intraday Metrics
`blood_pressure_diastolic` `blood_pressure_systolic` `body_temperature` `heartrate` `skin_temperature` `spo2` `vo2max` `heartrate_max` `heartrate_min` `spo2_max` `spo2_min` `body_temperature_max` `body_temperature_min` `skin_temperature_max` `skin_temperature_min` `blood_pressure_systolic_min` `blood_pressure_systolic_max` `blood_pressure_diastolic_min` `blood_pressure_diastolic_max` `calories_burned_active` `distance` `elevation` `elevation_max` `elevation_min` `steps` `ecg_voltage`
### Daily Metrics
`calories_burned_active` `calories_burned_basal` `distance` `duration_low_intensity` `duration_moderate_intensity` `duration_high_intensity` `elevation` `heartrate` `heartrate_max` `heartrate_min` `steps` `elevation_max` `elevation_min`
### Sleep Metrics
`bedtime_duration` `breathing_rate` `breathing_rate_max` `breathing_rate_min` `heartrate` `heartrate_max` `heartrate_min` `sleep_duration` `sleep_duration_awake` `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_efficiency` `sleep_latency` `hrv_rmssd` `hrv_sdnn` `sleep_interruptions`
### Workout Metrics
`calories_burned_active` `distance` `elevation` `heartrate` `heartrate_max` `heartrate_min` `spo2` `steps` `elevation_max` `elevation_min` `spo2_max` `spo2_min`
### User Properties
`body_bone_mass` `body_fat` `height` `weight` `body_fat_min` `body_fat_max`
### Backfill Limitations
Up to 90 days available. Recommend 30 days or less for consistency.
***
# Statistic Types and Methods
Source: https://docs.spikeapi.com/technical-references/statistics_matrix
This document provides a comprehensive overview of all statistics, detailing their methods, metrics, units, precision, and descriptions. The table below summarizes the key features supported by each statistic.
## Statistics
The following table lists all the statistics available, along with their descriptions, applicable methods, associated metrics, units of measurement, and number of decimals for output precision.
| **Label** | **Description** | **Methods** | **Metrics** | **Unit** | **Precision** |
| :---------------------------------- | :----------------------------------------------------------------- | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------- | :------------ |
| calories\_burned\_active | Calories burned during active periods | intersect\_sum | `calories_burned_active` | kcal | 4 |
| calories\_burned\_basal | Calories burned at rest (basal metabolic rate) | intersect\_sum | `calories_burned_basal` | kcal | 4 |
| calories\_burned\_total | Total calories burned including basal and active periods | intersect\_union\_sum | `calories_burned` `calories_burned_basal` `calories_burned_active` | kcal | 4 |
| distance\_cycling | Total distance covered while cycling | intersect\_sum | `distance_cycling` | meters | 4 |
| distance\_running | Total distance covered while running | intersect\_sum | `distance_running` | meters | 4 |
| distance\_total | Total distance covered across all activities | intersect\_union\_sum | `distance` `distance_walking` `distance_cycling` `distance_running` | meters | 4 |
| distance\_walking | Total distance covered while walking | intersect\_sum | `distance_walking` | meters | 4 |
| duration\_high\_intensity | Duration of high level intensity | intersect\_sum | `duration_high_intensity` | ms | 0 |
| duration\_low\_intensity | Duration of low level intensity | intersect\_sum | `duration_low_intensity` | ms | 0 |
| duration\_moderate\_intensity | Duration of medium level intensity | intersect\_sum | `duration_moderate_intensity` | ms | 0 |
| heartrate | Average heart rate over a period | duration\_weighted\_avg | `heartrate` | bpm | 0 |
| heartrate\_max | Maximum recorded heart rate, providing insights into peak values | max | `heartrate` | bpm | 0 |
| heartrate\_min | Minimum recorded heart rate, providing insights into lowest values | min | `heartrate` | bpm | 0 |
| heartrate\_resting | Average resting heart rate | duration\_weighted\_avg | `heartrate_resting` | bpm | 0 |
| hrv\_rmssd | Heart rate variability measured by RMSSD | duration\_weighted\_avg | `hrv_rmssd` | ms | 0 |
| hrv\_sdnn | Heart rate variability measured by SDNN | duration\_weighted\_avg | `hrv_sdnn` | ms | 0 |
| sleep\_duration\_total | Total sleep duration across all sleep stages | intersect\_union\_sum | `sleep_duration_deep` `sleep_duration_light` `sleep_duration_rem` `sleep_duration_awake` | ms | 0 |
| sleep\_score | Overall sleep score based on various sleep metrics | sleep\_score | `bedtime_duration` `sleep_duration` `sleep_duration_nap` `sleep_duration_light` `sleep_duration_deep` `sleep_duration_rem` `sleep_duration_awake` `sleep_interruptions` `sleep_efficiency` `sleep_latency` `sleep_breathing_rate` | count | 0 |
| sleep\_skin\_temperature\_deviation | Sleep skin temperature deviation from the baseline | intersect\_sum | `sleep_skin_temperature_deviation` | celsius | 2 |
| steps | Total number of steps taken | intersect\_sum | `steps` | count | 0 |
## Methods
The table below provides details on the various methods used in the statistics, including a brief description of each.
| **Label** | **Description** |
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------- |
| duration\_weighted\_avg | Average of values weighted by their duration, ensuring longer durations have a greater impact on the average. |
| intersect\_sum | Sum of intersecting values of specified metrics, aggregating overlapping data points to provide a total count or sum. |
| intersect\_union\_sum | Sum of the union of intersecting values, merging several metrics to provide a comprehensive aggregation across multiple metrics. |
| max | Maximum value of the specified metrics, providing insights into the peak values recorded. |
| min | Minimum value of the specified metrics, providing insights into the lowest values recorded. |
| sleep\_score | A score representing the quality of sleep, calculated using intersecting values. |