Nutrition Platform API
Build nutrition-aware applications with food recognition, barcode scanning, and intelligent search.
Introduction
The Nutrition Platform API provides programmatic access to food recognition via AI-powered photo analysis, global barcode-based product lookup, nutrition database search, and food logging. All API responses follow a consistent JSON envelope format with built-in rate limiting metadata.
This documentation covers the REST API and the official iOS / Swift SDK.
No version prefix (/v1/) is currently used — the API is accessed directly at the root.
Base URL
| Environment | Base URL |
|---|---|
| Production | https://arsalanbackup.tailad6cf6.ts.net |
| Local Development | http://localhost:3000 |
/v1/ prefix on any route. All endpoints are accessed directly at the root path.
Authentication
Most API endpoints require authentication via an API key. Keys are issued per organization and are passed
in the Authorization header using the Bearer scheme.
API Key Format
Keys follow the format sk_<random_hex> and are
scoped to a single organization. You can manage your API keys from the
Dashboard.
httpAuthorization: Bearer sk_a1b2c3d4e5f6...7890abcdef
Example: Authenticated Request
curlcurl https://arsalanbackup.tailad6cf6.ts.net/food/search?query=chicken \ -H "Authorization: Bearer sk_your_api_key_here"
Response Format
All API responses follow a consistent JSON envelope:
✅ Success
json{ "success": true, "data": { /* domain object */ }, "meta": { "request_id": "uuid", "ratelimit_limit": 1000, "ratelimit_used": 42, "ratelimit_remaining": 958, "usage_warning": null } }
❌ Error
json{ "success": false, "error": "Human-readable message", "code": "ERROR_CODE", "details": null }
Response Headers
| Header | Description |
|---|---|
X-Request-ID | Unique request identifier for tracing |
X-RateLimit-Limit | Monthly API call limit for the organization |
X-RateLimit-Used | API calls used this month |
X-RateLimit-Remaining | Calls remaining in the current period |
X-Usage-Warning | Present when usage ≥ 80%, value: approaching_limit |
Error Codes & HTTP Status
| HTTP Status | Code | Meaning |
|---|---|---|
400 | VALIDATION_ERROR | Request body or parameters are invalid |
400 | INVALID_JSON | Request body is not valid JSON |
400 | INVALID_BARCODE | Barcode format is invalid |
400 | MISSING_QUERY | Search query parameter is missing |
401 | UNAUTHORIZED | Missing or invalid API key |
403 | FORBIDDEN | Valid auth but insufficient permission |
404 | NOT_FOUND | Resource does not exist |
413 | BODY_TOO_LARGE | Request body exceeds 3 MB limit |
429 | RATE_LIMITED | Monthly API call limit exceeded |
500 | SERVER_ERROR | Unexpected server error |
502 | VISION_ERROR | AI model failed or returned invalid data |
Rate Limits
Rate limits are enforced on a monthly basis per organization. The limits depend on your subscription tier:
| Tier | Monthly Limit | Price |
|---|---|---|
| Free | 100 calls | Free |
| Starter | 1,000 calls | $29/month |
| Pro | 10,000 calls | $99/month |
| Enterprise | 100,000+ calls | Custom pricing |
When you exceed 80% of your monthly limit, the response includes a
X-Usage-Warning: approaching_limit header. Once the limit is exceeded,
subsequent requests return 429 RATE_LIMITED until the next billing cycle.
Endpoints
GET /health No Auth
Returns server health and status information. Useful for monitoring and connectivity checks.
cURL Example
curlcurl https://arsalanbackup.tailad6cf6.ts.net/health
Response
json{ "success": true, "data": { "status": "ok", "version": "0.1.0", "uptime": 1234.5, "environment": "production", "database": "connected", "db_pool": { "active_connections": 3, "latency_ms": 12 } }, "meta": { "request_id": "...", /* ... */ } }
GET /food/search?query=<query> Auth Required
Search the nutrition database for food items.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Search text (e.g. "chicken breast") |
limit | number | No | Results per page (default: 20, max: 100) |
source | string | No | Data source: local, openfoodfacts, usda, or all (default) |
page | number | No | Page number (default: 1) |
cURL Example
curlcurl "https://arsalanbackup.tailad6cf6.ts.net/food/search?query=chicken+breast&limit=5&source=openfoodfacts" \ -H "Authorization: Bearer sk_your_api_key"
Response
json{ "success": true, "data": { "data": [ { "id": "uuid", "name": "Chicken Breast, Boneless Skinless", "brand": "Perdue", "calories_100g": 165, "protein_g_100g": 31, "carbs_g_100g": 0, "fat_g_100g": 3.6, "fiber_g_100g": 0, "sugar_g_100g": 0, "sodium_mg_100g": 74, "source": "usda" } ], "total": 42, "page": 1, "limit": 5, "has_more": true }, "meta": { /* rate limit info */ } }
data field in the response contains a nested data array with the search results. The total, page, limit, and has_more fields provide pagination metadata.
GET /barcode/<barcode> Auth Required
Look up a product by its barcode (EAN-13 or UPC). Returns detailed nutrition information per 100g.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
barcode | string | Yes | 8–13 digit numeric barcode (EAN-13 or UPC) |
cURL Example
curlcurl https://arsalanbackup.tailad6cf6.ts.net/barcode/5901234123457 \ -H "Authorization: Bearer sk_your_api_key"
Response
json{ "success": true, "data": { "id": "uuid", "name": "Whole Milk", "brand": "Organic Valley", "barcode": "5901234123457", "calories_100g": 61, "protein_g_100g": 3.2, "carbs_g_100g": 4.8, "fat_g_100g": 3.3, "fiber_g_100g": 0, "sugar_g_100g": 4.8, "sodium_mg_100g": 50, "serving_size": "1 cup (240ml)", "serving_g": 240, "image_url": "https://...", "source": "open_food_facts" }, "meta": { /* rate limit info */ } }
POST /log Auth Required
Create a food log entry for a user. If the user_external_id does not exist in your organization, it will be auto-created.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
user_external_id | string | Yes | Your own unique user identifier |
food_name | string | Yes | Name of the food |
source | string | Yes | photo, barcode, search, or manual |
serving_qty | number | Yes | Number of servings (e.g. 1.5) |
meal_type | string | Yes | breakfast, lunch, dinner, or snack |
calories | number | No | Total calories for the entry |
protein_g | number | No | Total protein in grams |
carbs_g | number | No | Total carbohydrates in grams |
fat_g | number | No | Total fat in grams |
serving_unit | string | No | Unit (default: "serving") |
brand | string | No | Brand name (optional) |
barcode | string | No | Associated barcode |
logged_at | string | No | ISO 8601 timestamp (default: now) |
cURL Example
curlcurl https://arsalanbackup.tailad6cf6.ts.net/log \ -H "Authorization: Bearer sk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "user_external_id": "user_abc123", "food_name": "Grilled Chicken Salad", "source": "manual", "serving_qty": 1, "meal_type": "lunch", "calories": 420, "protein_g": 35, "carbs_g": 12, "fat_g": 24 }'
Response
json{ "success": true, "data": { "id": "log_entry_uuid", "user_id": "internal_user_uuid", "org_id": "org_uuid", "food_name": "Grilled Chicken Salad", "meal_type": "lunch", "source": "manual", "serving_qty": 1, "serving_unit": "serving", "calories": 420, "protein_g": 35, "carbs_g": 12, "fat_g": 24, "logged_at": "2026-05-07T22:00:00Z", "created_at": "2026-05-07T22:00:00Z" }, "meta": { /* rate limit info */ } }
GET /log/<user_external_id> Auth Required
Retrieve all food log entries for a given user. Only returns entries that have not been deleted (soft delete).
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
date | string | No | Filter by date in YYYY-MM-DD format |
cURL Example
curlcurl "https://arsalanbackup.tailad6cf6.ts.net/log/user_abc123?date=2026-05-07" \ -H "Authorization: Bearer sk_your_api_key"
Response
json{ "success": true, "data": [ { "id": "log_entry_uuid", "food_name": "Grilled Chicken Salad", "meal_type": "lunch", "calories": 420, "logged_at": "2026-05-07T22:00:00Z" // ... full FoodLogEntry object } ], "meta": { /* rate limit info */ } }
DELETE /log/<id> Auth Required
Delete a food log entry. This is a soft delete — the entry is hidden from GET /log but retained in the database for recovery and auditing.
cURL Example
curlcurl -X DELETE https://arsalanbackup.tailad6cf6.ts.net/log/log_entry_uuid \ -H "Authorization: Bearer sk_your_api_key"
Response
json{ "success": true, "data": { "deleted": true }, "meta": { /* rate limit info */ } }
POST /analyze/photo Auth Required
Analyze a food photo using a vision AI model. The model identifies foods, estimates portion sizes, and calculates nutritional breakdown.
image_base64 string must be a valid JPEG image encoded as base64. PNG and other formats are not accepted. The decoded image size must not exceed 3 MB.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
image_base64 | string | Yes | Base64-encoded JPEG image only (max 3 MB decoded) |
meal_type | string | No | breakfast, lunch, dinner, or snack |
cURL Example
curlcurl https://arsalanbackup.tailad6cf6.ts.net/analyze/photo \ -H "Authorization: Bearer sk_your_api_key" \ -H "Content-Type: application/json" \ -d '{ "image_base64": "/9j/4AAQSkZJRg...base64_encoded_image_data...", "meal_type": "dinner" }'
Response
json{ "success": true, "data": { "foods": [ { "name": "Grilled Chicken Breast", "estimated_portion": "150g", "calories": 248, "protein_g": 46.5, "carbs_g": 0, "fat_g": 5.2, "fiber_g": 0, "confidence": "high" }, { "name": "Roasted Vegetables", "estimated_portion": "100g", "calories": 85, "protein_g": 2.1, "carbs_g": 15, "fat_g": 2.5, "fiber_g": 4, "confidence": "medium" } ], "meal_description": "A grilled chicken breast with roasted vegetables" }, "meta": { "cost": 0.005, "tokens": 450, "latency_ms": 2340, "request_id": "req_abc123", "ratelimit_remaining": 957 } }
GET /usage Auth Required
Retrieve current usage statistics for your organization, including call counts, cost estimates, and daily breakdown.
cURL Example
curlcurl https://arsalanbackup.tailad6cf6.ts.net/usage \ -H "Authorization: Bearer sk_your_api_key"
Response
json{ "success": true, "data": { "org_id": "uuid", "tier": "starter", "monthly_limit": 1000, "used_this_month": 342, "remaining": 658, "estimated_cost_usd": 12.45, "daily_breakdown": [ { "date": "2026-05-07", "photo_analyses": 10, "barcode_scans": 5, "text_searches": 2, "total": 17 } ] }, "meta": { /* rate limit info */ } }
iOS / Swift SDK
The official iOS SDK provides a native Swift interface to the Nutrition Platform API. It is distributed via Swift Package Manager (SPM).
Installation
Add the package to your Package.swift or Xcode project:
Via Xcode
- File → Add Package Dependencies...
- Enter:
https://github.com/iArsalanshah/nutrition-platform - Select "Up to Next Major" version and add the NutritionPlatform library to your target
Via Package.swift
swiftdependencies: [ .package( url: "https://github.com/iArsalanshah/nutrition-platform", from: "0.1.0" ) ]
Initialization
swiftimport NutritionPlatform // Initialize with your API key let client = NutritionClient( apiKey: "sk_your_api_key_here", baseUrl: "https://arsalanbackup.tailad6cf6.ts.net" )
baseUrl points to a staging/v1 URL but you should set it to the production
URL shown above during setup.
Client Methods
analyzePhoto
Analyze a food photo using a vision AI model.
swiftlet result = try await client.analyzePhoto( imageBase64: base64String, mealType: "dinner" ) // Access detected foods for food in result.data.foods { print(food.name) // e.g. "Grilled Chicken Breast" print(food.calories) // 248.0 print(food.confidence) // "high" } // Meal description print(result.data.mealDescription) // Response metadata (rate limits, model info, etc.) print(result.meta.requestId)
lookupBarcode
Look up a product by barcode.
swiftlet product = try await client.lookupBarcode("5901234123457") print(product.data.name) // "Whole Milk" print(product.data.calories100g) // 61.0 print(product.data.brand) // "Organic Valley"
searchFood
Search the nutrition database for food items.
swiftlet searchResults = try await client.searchFood( query: "chicken breast", source: "all", page: 1, limit: 20 ) print(searchResults.data.total) // Total results count print(searchResults.data.hasMore) // true if more pages for item in searchResults.data.results { print(item.name) print(item.calories100g) }
createLogEntry
Create a food log entry.
swiftlet entry = CreateLogEntryRequest( userExternalId: "user_abc123", foodName: "Grilled Chicken Salad", mealType: "lunch", source: "manual", servingQty: 1, servingUnit: "bowl", calories: 420, proteinG: 35, carbsG: 12, fatG: 24 ) let logEntry = try await client.createLogEntry(entry) print(logEntry.data.id) // The created entry's ID print(logEntry.data.loggedAt) // ISO 8601 timestamp
getLogs
Retrieve food log entries for a user.
swift// Get all logs for a user let logs = try await client.getLogs(forUser: "user_abc123") // Filter by date let todaysLogs = try await client.getLogs( forUser: "user_abc123", date: "2026-05-07" ) for entry in logs.data { print(entry.foodName) print(entry.calories) }
deleteLogEntry
Delete a food log entry (soft delete).
swifttry await client.deleteLogEntry(id: "log_entry_uuid") // Entry is soft-deleted — hidden from getLogs but retained for auditing
Error Handling
All SDK methods throw NutritionError on failure. Use try/catch to handle errors:
swiftdo { let result = try await client.analyzePhoto(imageBase64: image) // Handle success } catch let error as NutritionError { switch error { case .httpError(let status, let message, let code): print("HTTP \(status): \(message) [\(code ?? "N/A")]") case .decodingError(let underlying): print("Failed to parse response: \(underlying)") case .networkError(let underlying): print("Network error: \(underlying)") case .invalidURL: print("Invalid URL configured") case .maxRetriesExceeded: print("Request failed after retries") } } catch { print("Unexpected error: \(error)") }
Response Metadata
Every SDK method returns a NutritionResponse<T> wrapper with a meta property containing rate limit information:
swiftlet result = try await client.searchFood(query: "apple") // Rate limit info from response headers print(result.meta.requestId) // UUID for tracing print(result.meta.rateLimitRemaining) // Calls remaining print(result.meta.usageWarning) // "approaching_limit" or nil print(result.meta.retryAfter) // Seconds to wait (on 429)
Model Types
The SDK defines the following key models (all Codable and Sendable):
| Type | Description |
|---|---|
PhotoAnalysisResult | Contains foods: [RecognizedFood] and mealDescription: String |
RecognizedFood | A food item identified in a photo with nutrition estimates |
BarcodeProduct | Product details from a barcode lookup |
FoodSearchResult | A single search result item |
SearchResult | Paginated search results with results, total, page, limit, hasMore |
CreateLogEntryRequest | Request body for creating a log entry |
FoodLogEntry | A food log entry with full nutrition breakdown |
NutritionResponse<T> | Generic response wrapper with data and meta |
ResponseMeta | Rate limit metadata from response headers |
NutritionError | Error enum with httpError, decodingError, networkError, etc. |
Full Example
swiftimport NutritionPlatform let client = NutritionClient( apiKey: "sk_your_api_key", baseUrl: "https://arsalanbackup.tailad6cf6.ts.net" ) do { // 1. Search for a food let search = try await client.searchFood(query: "oatmeal", limit: 5) print("Found \(search.data.total) results") // 2. Create a log entry let entry = CreateLogEntryRequest( userExternalId: "user_abc", foodName: "Oatmeal with Berries", mealType: "breakfast", source: "search", servingQty: 1, calories: 320, proteinG: 12, carbsG: 54, fatG: 5 ) let created = try await client.createLogEntry(entry) print("Created log entry: \(created.data.id)") // 3. List today's logs let logs = try await client.getLogs(forUser: "user_abc", date: "2026-05-07") print("Today's entries: \(logs.data.count)") // 4. Check usage // (no SDK method for /usage — use direct API call if needed) } catch { print("Error: \(error)") }
NutritionResponse via the meta property — check result.meta.rateLimitRemaining to monitor your usage from within your app.
Contact & Support
Have questions, need help integrating, or want to report an issue? Our team is here to help.
Send us a message at support@nutritionplatform.com and we'll get back to you within 24 hours.
Nutrition Platform API — v0.1.0
↑ Back to Top