From a photo
to a working tool.
Create a private key, ask its owner to fund it, then return structured nutrition data. The runnable client saves request IDs and results so an interrupted connection does not create a second paid scan.
1. Prepare a private key
The client uses Node 22 or later and has no package dependencies. Review the downloaded source before running it. It communicates only with https://kcalsnap.com/api/v1.
curl --fail --output quickstart.mjs https://kcalsnap.com/developers/quickstart.mjs
node quickstart.mjs --help
node quickstart.mjs initThis creates .kcalsnap/key with owner-only file permissions and a local .gitignore. Back up the complete .kcalsnap directory securely before funding it and keep that backup current, including checkout and request state. KcalSnap cannot reconstruct a lost key. Keep the entire directory out of version control, logs and chat messages.
Already downloaded a key from the website? Store it privately, restrict its permissions, then use --key-file=/private/path/key.txt on every command. Skip init; credits stay with their original key.
chmod 600 /private/path/key.txt
node quickstart.mjs balance --key-file=/private/path/key.txt2. Fund it with the owner's approval
node quickstart.mjs checkoutThe response contains a Stripe checkout_url for a $5 pack of 500 successful scans, including applicable tax. Give that URL to the cardholder and wait for them to complete it. The command creates a link; it does not pay or enter card details. Keep the same key and saved order for retries.
# After the cardholder completes payment:
node quickstart.mjs balanceContinue once data.credits_remaining is positive and data.blocked is false. A brief webhook delay is possible. Before the first checkout a key is unknown to the server, so a balance check can return 401 invalid_key. Do not repeatedly generate replacement keys.
For a later, explicitly authorized top-up, run checkout --new-order. It checks the previous order with the server and refuses a new link while the old checkout is still payable or unresolved. A paid order, a Stripe-confirmed expired session, or an old order with a complete lookup showing no session permits a new link. The no-session path waits at least 48 hours; incomplete lookups remain unresolved. Contact support with the order ID if a previous checkout cannot be recovered. There is no automatic top-up or subscription. Unused credits do not expire. Agents with an authorized Base USDC wallet can instead use x402 for autonomous per-call payment.
3. Analyze one photo
node quickstart.mjs scan lunch.jpgUse JPEG or PNG, no larger than 2 MB, with each side from 16 to 2048 pixels. The client returns JSON containing status, data.request_id, detected foods, estimated grams, calories and macros. A successful new scan consumes one credit. A failed analysis returns its reserved credit.
To try the same photo shown on our developer page:
curl --fail --output sample.png https://kcalsnap.com/developers/examples/dish_1566414291.png
node quickstart.mjs scan sample.pngThis is a live paid analysis. To inspect the product without spending a credit, use the saved example catalog. Its images come from Nutrition5k under CC BY 4.0; each JSON file includes attribution and measured-reference values.
Build a review step into food logging: show the detected foods, estimated portions and nutrition source before saving. Hidden oil and portion size remain uncertain. model_confidence is not a calibrated accuracy probability. These estimates do not establish medical nutrition needs or allergen safety. Read our measured limits.
4. Recover safely and reuse results
If a connection drops, repeat the same command with the same photo and key. The client saves an idempotency key before sending and keeps it across failures. Honor a returned retryAfter value; do not loop rapidly on capacity limits.
# Safe retry or free replay of a locally saved response:
node quickstart.mjs scan lunch.jpg
# Only when you deliberately want a new paid estimate:
node quickstart.mjs scan lunch.jpg --new-scanCompleted responses are cached in private local files next to the key. Repeating the command reuses them without a network call. The files include food text and nutrition results; protect or delete them when no longer needed. Photos are not copied into that cache.
Unresolved requests keep their original ID. The client refuses to replace them with a new paid request, and stops automatic recovery after 24 hours. Contact hello@kcalsnap.com with the displayed Idempotency-Key and request ID if available, never your API key. SIGINT/SIGTERM release the lock. A hard-killed or crashed process may leave a .lock file containing its process ID and host; confirm that process is no longer running before removing only that lock.
Checkout recovery state is local to this integration. Restoring only the key or using multiple independent clients can lose the original order ID; check existing links and balance before authorizing another payment. Keep the same working directory or use an absolute --key-file path on every run. Each changed image is a different request. Resizing or editing an image during a retry creates a different input. A successful response saved locally remains reusable after the server's 24-hour result window; keep your local state intact.
5. Collect actual feedback
With a card-credit key, submit feedback on a completed request owned by that key:
node quickstart.mjs feedback REQUEST_UUID wrong_portionCategories: helpful, wrong_food, wrong_portion, wrong_nutrition, integration_problem, other. Agents should pass along real user feedback, not invent ratings. Feedback does not refund credits or automatically alter a model.
The POST /api/v1/credits/feedback REST endpoint also accepts an optional note up to 500 characters. Use the same Bearer key and {"request_id":"UUID", "category":"wrong_portion", "note":"Optional explanation"}. Never include keys or personal information. One submission per request, at most 10 per key per rolling day; identical retries are accepted. Requests must be completed within the past 90 days. Feedback is removed after 90 days when feedback is next submitted. x402 callers can contact support with their request ID.
Use it in an agent integration
Keep payment authorization separate from the tool's scanning permission. Read keys from private files or a secrets manager and send them only in the Authorization header. Persist one request ID per intended analysis before the first HTTP request. Save successful responses in your own cache if you need later comparisons.
For MCP, configure the same Bearer key on the connection to https://kcalsnap.com/mcp. The analyze_food tool accepts image_base64, mime_type, language and idempotency_key. Feedback uses REST.
Machine-readable instructions · OpenAPI schema · Payment, capacity and error reference · Browser checkout and first scan