Food analysis
Scope: food_analysis:write (plus food_logs:write to log the result). user is the scoped client.
Analyze a meal and log it
import { preparePhotoScanImage, type FoodSelection } from '@januaryai/web-sdk';
const file = fileInput.files?.[0];
if (!file) throw new Error('Choose a meal photo');
const { dataUri } = await preparePhotoScanImage(file);
const scan = await user.foodAnalysis.analyzePhoto({ image: dataUri, signal });
// Or a hosted image: analyzePhoto({ image: 'https://cdn.example.com/meal.jpg' })
// Or text: analyzeDescription({ query: 'two eggs and a slice of toast' })
const foods: FoodSelection[] = scan.detections.map(({ food }) => ({
id: food.id,
serving: { id: food.serving.id, quantity: food.quantity },
}));
if (foods.length > 0) {
await user.foodLogs.create({ foods, name: scan.mealName ?? undefined });
}Input.
analyzePhototakes a base64 data URI or a publicly fetchablehttp(s)URL.preparePhotoScanImagebuilds the data URI in the browser: it keeps the aspect ratio, limits the longest edge to 1,000 pixels, and compresses to JPEG at quality 0.7. On a server, pass a URL or a data URI you prepare there.analyzeDescriptiontakes a text description of up to 512 characters instead.Time. A complex meal can take tens of seconds. Show progress, and pass a
signalso the user can cancel.Image errors are
validationerrors:image_unreachable,image_corrupt,image_format_unsupported, andimage_invalid_base64. Retrying the same image fails the same way. A photo of only a barcode is rejected; usefoods.lookupBarcode.Result.
detectionsis empty when nothing was recognized, andmealNamecan benull. Each detection'squantitycounts servings ofserving, and itsnutrientsare already scaled to that quantity.
Correct a result
Send the result back exactly as it was returned, with a plain-language instruction:
Keep meal images and nutrition out of analytics, crash reports, and general logs.
Last updated
Was this helpful?