Bitefull API
A REST API for recipes, AI recipe import, meal planning, shopping lists, and pantry management.
Discover and author recipes, import them from any link or photo, generate weekly meal plans around dietary goals, turn plans into de-duplicated shopping lists, and track pantry inventory. JSON over HTTPS.
/v2.
Base URL & versioning
Every endpoint lives under a versioned prefix:
https://api.kyox.io/bitefull/v1
The version is part of the path. Breaking changes ship under a new version (/v2) while older versions keep working. The interactive reference is at /bitefull/v1/docs.
Authentication
Bitefull uses two layers. An API key identifies the calling app or account and is required on every request. A user token (JWT) identifies an end-user and is required only on user-scoped routes.
API key
Send your key in the X-API-Key header on every request. Keys gate access to the API and are how usage is metered per account.
X-API-Key: bk_live_<your-key>
Manage keys at /api-keys (create, list, revoke). A key's secret is shown once at creation — store it securely; it can't be retrieved again. Requests without a valid key return 401.
User token
Register or log in to receive a short-lived access token and a rotating refresh token. Send the access token alongside your API key on authenticated routes:
X-API-Key: bk_live_<your-key> Authorization: Bearer <accessToken>
Access tokens expire after 15 minutes. Exchange a refresh token at POST /auth/refresh for a new pair. Refresh tokens rotate on every use — reusing an old one revokes the whole session family.
Quick start
# Register a user (API key required on every call) curl -X POST https://api.kyox.io/bitefull/v1/auth/register \ -H "X-API-Key: bk_live_<your-key>" \ -H "Content-Type: application/json" \ -d '{"email":"you@example.com","username":"chef","password":"Secret123"}' # Call a user-scoped route with both credentials curl https://api.kyox.io/bitefull/v1/auth/me \ -H "X-API-Key: bk_live_<your-key>" \ -H "Authorization: Bearer <accessToken>"
Roles: USER, PREMIUM, ADMIN — used for authorization and rate-limit tiers.
Errors
Errors use standard HTTP status codes and a consistent JSON envelope:
{
"statusCode": 401,
"error": "UnauthorizedException",
"message": "Invalid credentials",
"path": "/bitefull/v1/auth/login",
"timestamp": "2026-06-01T12:00:00.000Z"
}
| Code | Meaning |
|---|---|
400 | Bad request |
401 | Missing or invalid token |
403 | Authenticated but not allowed |
404 | Resource not found |
409 | Conflict (e.g. email taken) |
422 | Validation failed (message is a list) |
429 | Rate limit exceeded |
503 | Feature not configured on this server (e.g. AI image import) |
Rate limits
Limits scale with your tier, counted per rolling 60-second window:
| Tier | Requests / minute |
|---|---|
| Anonymous | 60 |
| USER | 300 |
| PREMIUM / ADMIN | 1200 |
Exceeding the limit returns 429 Too Many Requests.
API Keys
Issue and manage the keys that gate API access. These routes require a user token; the very first key is minted server-side.
Create a key
POST /api-keys
{ "name": "Acme production", "expiresInDays": 365 }
The response includes key exactly once — store it now. Optionally set rateLimitPerMin to override the tier limit for a paid plan.
Auth
Account recovery & verification
Example response
{
"accessToken": "eyJhbGciOi...",
"refreshToken": "k3jf9...",
"tokenType": "Bearer",
"expiresIn": 900
}
Recipes
The catalog ships with 762 published recipes across 42 cuisines — every one with a real image, instructions, an ingredient list, and estimated per-serving nutrition.
Search
Combine free-text with structured filters. All parameters are optional:
| Param | Example | Notes |
|---|---|---|
q | salmon | Full-text over title, cuisine, tags, description |
diet | VEGAN | Repeatable; recipe must match all |
tags | breakfast | Repeatable; recipe must carry all (meal types, descriptors) |
cuisine | Mediterranean | |
maxTime | 30 | Total minutes |
minCalories / maxCalories | 300 / 700 | Per serving |
difficulty | EASY | EASY · MEDIUM · HARD |
sort | rating | relevance · rating · popular · newest · quickest |
page / limit | 1 / 20 | Pagination |
curl "https://api.kyox.io/bitefull/v1/recipes/search?q=salmon&diet=PESCATARIAN&maxTime=40"
Filtering lives on /recipes/search. The plain GET /recipes list only paginates — it ignores cuisine, diet, tags and the other filters.
Browse & categories
GET /recipes/categories returns the facets for building browse menus and home sections. Each entry is { value, count } (meal types also include a label), plus a filterParam map naming the /recipes/search parameter that applies each facet:
| Facet | Apply on /recipes/search via |
|---|---|
cuisines | ?cuisine= |
mealTypes | ?tags= |
diets | ?diet= |
difficulties | ?difficulty= |
popularTags | ?tags= |
curl "https://api.kyox.io/bitefull/v1/recipes/categories" curl "https://api.kyox.io/bitefull/v1/recipes/search?cuisine=Thai&tags=dinner&sort=rating"
Create a recipe
POST /recipes
{
"title": "Grilled Chicken Bowl",
"servings": 2,
"prepTime": 10, "cookTime": 20,
"difficulty": "EASY",
"cuisine": "American",
"diets": ["DAIRY_FREE"],
"tags": ["high-protein"],
"ingredients": [
{ "name": "chicken breast", "quantity": 300, "unit": "GRAM" },
{ "name": "white rice", "quantity": 200, "unit": "GRAM" }
],
"instructions": [
{ "step": 1, "text": "Cook the rice." },
{ "step": 2, "text": "Grill the chicken." }
],
"publish": true
}
Per-serving nutrition (calories, protein, carbs, fat, fiber, sodium) is computed automatically from ingredient data.
Engagement
AI Import
Turn a link or a photo into a pre-filled, editable recipe draft.
These routes need only your X-API-Key — no user token — so an app can offer import before sign-up. The response is a RecipeDraft that mirrors the POST /recipes body field-for-field: drop it into a review form, let the user fix anything, then create the recipe normally.
Import from a link
# Most recipe sites embed schema.org Recipe JSON-LD — parsed directly, no LLM needed. # Pages without structured data fall back to an LLM. Results are cached per URL for 24h. curl -X POST https://api.kyox.io/bitefull/v1/import/url \ -H "X-API-Key: bk_live_<your-key>" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com/best-lasagna-recipe"}'
Import from a photo
# JPEG / PNG / WebP / HEIC, up to 10 MB. Dish photos, handwritten cards, printed pages.
curl -X POST https://api.kyox.io/bitefull/v1/import/image \
-H "X-API-Key: bk_live_<your-key>" \
-F "image=@recipe-photo.jpg"
The RecipeDraft
{
"draft": { "title": "Best Lasagna", "servings": 8, "ingredients": [ ... ], ... },
"source": "jsonld",
"warnings": ["2 ingredient quantities could not be parsed"]
}
source—jsonld(structured data),llm(text extraction), orvision(photo)- The draft is intentionally lenient: quantities may be
nulland unknown units pass through for the user to fix in review warningstells the review UI what to highlight- Nutrition is not in the draft — the server computes it when the confirmed recipe is created
Check GET /import/status at app start. url is always true; when AI is not configured on the server, image is false and POST /import/image returns 503 — hide photo import in that case. Some publishers block automated fetches; a failed URL import returns a clear error the user can work around by importing a photo instead.
Ingredients
A curated, USDA-backed ingredient catalog with per-100g nutrition. It powers the automatic per-serving nutrition on recipes and is handy for typeahead in recipe editors.
curl "https://api.kyox.io/bitefull/v1/ingredients?q=chick" \ -H "X-API-Key: bk_live_<your-key>" \ -H "Authorization: Bearer <accessToken>"
q is optional — omit it to list the catalog. Ingredient icons are served from the public media CDN at https://api.kyox.io/bitefull/media/.
Meal plans
Generate a plan
The generator fills each day and slot with recipes that satisfy dietary restrictions, hit a daily calorie band, prefer your cuisines and pantry-covered recipes, and avoid repeats. Unspecified options fall back to your saved preferences.
POST /meal-plans/generate
{
"startDate": "2026-06-08",
"days": 7,
"slots": ["BREAKFAST", "LUNCH", "DINNER"],
"diets": ["VEGAN"],
"dailyCalorieGoal": 1800,
"cuisines": ["Mediterranean"],
"usePantry": true
}
Shopping lists
Generate from a meal plan
Collects every ingredient across the chosen recipes (or a whole meal plan), normalizes units, aggregates duplicates, and subtracts what your pantry already covers.
POST /shopping-lists/generate
{ "mealPlanId": "8bc883fc-...", "subtractPantry": true }
Pantry
POST /pantry
{ "ingredientName": "olive oil", "quantity": 500, "unit": "GRAM", "expirationDate": "2026-12-01" }
Users
Dietary preferences set here (diets, allergens, preferred cuisines, daily calorie goal) become the defaults for search, recommendations, and meal-plan generation.
Uploads
Recipe images upload directly to object storage via a presigned URL — image bytes never pass through the API. A background worker then generates optimized WebP variants.
Upload flow
# 1. Ask for a presigned URL POST /uploads/recipe-image { "contentType": "image/jpeg" } # → { "uploadUrl": "...", "key": "...", "publicUrl": "..." } # 2. PUT the file bytes straight to uploadUrl (no API key needed) # 3. Confirm — queues WebP variant generation and links the image POST /uploads/complete { "key": "<key from step 1>", "recipeId": "<recipe uuid>" }
Health
{ "status": "ok", "checks": { "api": "up", "database": "up" }, "timestamp": "..." }