API de consulta masiva de alimentos
Obtén hasta 100 alimentos en una sola petición. Envía un array de ID o de códigos de barras y recibe las fichas completas, además de la lista de lo que no se encontró: ideal para completar un plan de comidas, un carrito, el diario alimentario de un día o un catálogo de productos escaneados sin cien viajes de ida y vuelta.
curl -X POST -H "X-API-Key: food_•••" \
-H "Content-Type: application/json" \
-d '{"ids":["62ea12b8...","2fc13a3b..."]}' \
"https://foodbase.dev/v1/foods/bulk"
{
"data": [
{ "id": "62ea12b8...", "name_default": "Original Taste", "brand": "Coca-Cola" },
{ "id": "2fc13a3b...", "name_default": "Nutella", "brand": "Ferrero" }
],
"not_found": []
} ¿Prefieres hacer clic? Prueba el explorador de productos o la zona de pruebas de fotos.
Qué obtienes
ids[]
De 1 a 100 ID de alimentos en el cuerpo de la petición, o bien:
barcodes[]
De 1 a 100 códigos de barras (GTIN-8/12/13/14), cada uno resuelto como en la API de búsqueda por código de barras, incluidas las variantes con ceros a la izquierda.
data[]
Fichas completas en el orden pedido; las búsquedas por código de barras añaden requested_barcode (el código que enviaste).
not_found[]
Los ID o códigos de barras sin producto, sin error para el lote.
?lang=
Opcional: en/fr/es/de/it/bg, como en las consultas de una sola ficha.
Disponible en el plan Starter y superiores. Ver planes →
Ejemplos de código
La misma petición en curl, JavaScript y Python. Sustituye YOUR_API_KEY por una clave de tu panel.
curl "https://foodbase.dev/v1/foods/bulk" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ids":["62ea12b8-b260-c288-5c38-3a4906f5d6dd","2fc13a3b-1913-5ab6-d2c7-8c09b56f93bb"]}'
const res = await fetch("https://foodbase.dev/v1/foods/bulk", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
"ids": [
"62ea12b8-b260-c288-5c38-3a4906f5d6dd",
"2fc13a3b-1913-5ab6-d2c7-8c09b56f93bb"
]
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
import requests
res = requests.post(
"https://foodbase.dev/v1/foods/bulk",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"ids": [
"62ea12b8-b260-c288-5c38-3a4906f5d6dd",
"2fc13a3b-1913-5ab6-d2c7-8c09b56f93bb"
]
},
timeout=30,
)
res.raise_for_status()
data = res.json()
Cómo funciona
La petición, paso a paso
POST /v1/foods/bulk con Content-Type: application/json, tu cabecera X-API-Key y un cuerpo de la forma {"ids": [...]} o {"barcodes": [...]}: exactamente uno de los dos, de 1 a 100 entradas. Los ids son cadenas UUID (las de los ejemplos de código de arriba son fichas reales para probar); los códigos de barras se resuelven exactamente como en GET /v1/foods/barcode/{code}, de modo que un EAN-13 encuentra un producto guardado en su forma UPC-12 o GTIN-14, y el campo barcode de la ficha muestra la forma guardada junto a requested_barcode.
La validación se hace antes de tocar la base de datos y es de todo o nada. Un código de barras dentro de ids, los dos campos o ninguno, un array vacío o 101 entradas rechazan todo el lote con 422 {"error": "Validation Error", "details": [{"field": "ids.3", "message": ...}]}. Un ?lang= opcional (en, fr, es, de, it, bg) traduce las etiquetas de categoría y alérgenos como en las consultas de una sola ficha; los nombres e ingredientes en otros idiomas están en los arrays name e ingredients.
Cómo leer la respuesta
data es un array de objetos FoodItem construidos por el mismo formateador que la API de base de datos de alimentos, así que un parser escrito para el registro individual funciona sin cambios, desde id, barcode y source hasta categories_tags, image_url y allergens. Los datos de origen ausentes son null, nunca una clave ausente.
nutrition_summary contiene energy_kcal, proteins_g, carbs_g, fat_g, sugars_g y fiber_g por 100 g. Solo es null en su totalidad cuando el producto no tiene ninguna fila de nutrientes; una fila a la que le falta un valor muestra null solo para esa clave. El texto de etiqueta como quantity y serving_size_raw llega exactamente como lo tecleó el colaborador, así que analízalo a la defensiva.
Elementos que faltan, duplicados y deriva
La respuesta puede ser más corta que la petición: no hay un 404 por elemento, pero cada id o código de barras sin producto aparece en not_found, en el orden de la petición, así que nunca tienes que compararlo tú. Un id o código de barras repetido vuelve como una sola ficha: elimina duplicados antes de enviar para que cada una de las 100 posiciones cuente.
Los ids son deterministas, derivados de la fuente y de su propio identificador de producto, así que un producto conserva su id entre reimportaciones. Si un id guardado deja de resolverse, el producto ya no está en el catálogo con esa identidad: marca la fila como obsoleta y vuelve a resolverla con la API de consulta de códigos de barras o la API de búsqueda de alimentos en lugar de borrarla.
Cuota y cálculo de límites de tasa
Una llamada masiva es una petición contra la cuota diaria y un token del cubo por segundo, sea cual sea el número de ids. El orden del middleware es autenticación, límite por segundo, restricción de plan y después cuota diaria, así que un 401, un 429 por límite de tasa o el 403 de Starter nunca consumen una solicitud diaria. Un 422 sí, porque la validación se ejecuta después de contar la cuota; comprueba los UUID en el cliente.
Lee las cabeceras en cada 200: X-RateLimit-Second-Limit, X-RateLimit-Burst-Capacity y X-RateLimit-Second-Remaining para el cubo por clave, X-RateLimit-Account-Limit y X-RateLimit-Account-Remaining para el agregado por segundo de todas las claves de la cuenta, y X-RateLimit-Daily-Limit, X-RateLimit-Daily-Remaining y X-RateLimit-Daily-Reset (en cuentas normales) para el presupuesto diario compartido y su reinicio a medianoche UTC. El POST no lleva cabecera Cache-Control, así que nada se cachea en el borde por ti: guarda lo que obtengas.
Sincronizar un catálogo cada noche
Para el surtido de un minorista, envía directamente los códigos de barras, 100 por llamada, o guarda el id devuelto junto a tu SKU y sincroniza después por id. En ambos casos el proceso nocturno nunca busca: lee los códigos guardados, elimina duplicados, los divide en grupos de 100, envía cada grupo aquí y guarda un punto de control tras cada grupo terminado, para que un catálogo mayor que el presupuesto de una noche continúe donde se quedó. Compara updated_at con tu copia para saber qué ha cambiado.
Marca el ritmo con las cabeceras en lugar de una pausa fija, y vigila X-RateLimit-Daily-Remaining para que la sincronización no deje sin cuota al tráfico diurno de la misma cuenta. Cuando una vista necesite el panel de más de 40 nutrientes, llama a la API de nutrición para ese id bajo demanda; recibe un id por petición y también cuenta como una solicitud.
- Deduplica ids, divide en grupos de 100 y guarda un punto de control por grupo.
- Marca como obsoleto todo lo que esté en not_found; vuelve a comprobarlo más tarde.
- Obtén /nutrients de forma diferida, por producto, no en la sincronización.
Qué puedes crear
Planes de comidas y registros de alimentos
Resuelve cada elemento del plan de un día con una sola llamada.
Hidratación de carritos
Convierte una lista de IDs de alimentos guardados en registros completos de forma eficiente.
Trabajos de sincronización
Refresca muchos alimentos en caché por lotes con una programación.
Límites por plan
| Free | No disponible: 403 con required_plan starter (no cuenta contra las 100 solicitudes/día) |
|---|---|
| Starter | 5000 solicitudes/día, 5/s sostenidas, ráfaga de 25; hasta 100 ID o códigos de barras por petición |
| Pro | 25.000 solicitudes/día, 20/s sostenidas, ráfaga de 100; hasta 100 ID o códigos de barras por petición |
| Enterprise | 100.000 solicitudes/día, 50/s sostenidas, ráfaga de 250; hasta 100 ID o códigos de barras por petición |
Cada respuesta incluye cabeceras X-RateLimit-* con el presupuesto restante; cada 429 incluye Retry-After . Comparar planes →
Errores y cómo gestionarlos
403 — Cuenta gratuita; el cuerpo tiene error Plan upgrade required, required_plan starter, current_plan free y un upgrade_url
Mejora tu plan en el upgrade_url, o usa GET /v1/foods/{id} en el plan gratuito
422 — Ninguno o los dos campos ids y barcodes; un array vacío o de más de 100 elementos; un no-UUID en ids o un código de barras vacío; details[] indica la posición que falla
Valida y elimina duplicados en el cliente; envía los códigos de barras en barcodes[], no en ids[]
429 — El cubo por clave o por cuenta está vacío, o X-RateLimit-Daily-Remaining llegó a 0 (cuerpo: Daily quota exceeded)
Espera Retry-After segundos; en el caso diario, es el tiempo hasta medianoche UTC
401 — Falta la cabecera X-API-Key, o la clave no es válida o está revocada
Revisa el nombre de la cabecera y la clave en el panel
503 — El almacén de cuotas o de límites de tasa no está accesible; la API falla en modo cerrado
Reintenta con retroceso exponencial; la petición no se contó
Comparada con otras API
Frente al GET de un solo registro, la ventaja está en las idas y vueltas y la cuota: 100 registros por una petición en lugar de 100 peticiones. El GET gana cuando muchos clientes leen el mismo producto popular, porque sus respuestas son cacheables durante 300 segundos por cualquier caché HTTP y acepta lang; el masivo no se cachea y llena tu propio almacén. La API de búsqueda de alimentos y la API de consulta de códigos de barras convierten texto o un GTIN en un id; este endpoint convierte en registros los ids que ya tienes. La API de nutrición va en la dirección contraria: el panel completo de un producto en lugar de seis valores resumidos de hasta 100.
Preguntas frecuentes
¿Puedo enviar códigos de barras o términos de búsqueda en lugar de ids? +
No. Cada entrada debe ser un UUID o se rechaza todo el lote con 422. Resuelve los códigos de barras con la API de consulta de códigos de barras y los nombres con la API de búsqueda de alimentos, guarda los ids y úsalos aquí.
¿Una llamada con 100 ids cuesta 100 solicitudes? +
No, cuesta una. El contador diario y el cubo por segundo avanzan cada uno una unidad por petición, sea cual sea el número de ids que contenga.
¿Por qué no hay parámetro lang? +
El masivo devuelve el registro en bruto sin los campos auxiliares localizados. Los arrays name e ingredients contienen todos los idiomas que aportaron los colaboradores, así que elige la entrada en el cliente; si necesitas category_localized o allergens_localized, usa GET /v1/foods/{id}?lang= para ese producto.
¿Qué ve una cuenta gratuita? +
Un 403 con error Plan upgrade required, required_plan starter y un upgrade_url que apunta a la página de facturación. La restricción se ejecuta antes de la cuota diaria, así que esos 403 no consumen las 100 solicitudes al día, y GET /v1/foods/{id} sigue disponible.
¿Puedo obtener en masa el desglose completo de nutrientes? +
No. Los registros masivos llevan solo nutrition_summary. El panel de más de 40 nutrientes viene de la API de nutrición, un id por petición; desde Starter no tiene tope diario aparte, y en el plan gratuito se limita a 25 al día.
Descubre más endpoints
API de búsqueda de alimentos
Búsqueda de texto completo en 6,1 millones de productos alimentarios por nombre, marca o categoría: ordenada por relevancia, paginada y rápida. Busca en cualquier idioma en que se imprimió la etiqueta (cirílico incluido) y pasa lang=en|fr|es|de|it|bg para obtener nombres, categorías y alérgenos localizados. Una sola petición GET devuelve un JSON limpio que tu app puede mostrar al instante.
Más información →API de nutrición
Ve más allá de los macros principales. Obtén un desglose detallado de más de 40 nutrientes por producto (energía, proteínas, hidratos de carbono, grasas, azúcares, fibra, sodio, vitaminas y minerales), todo por 100 g, para cualquier alimento por ID.
Más información →API de base de datos de alimentos
Una única fuente para 6,1 millones de alimentos. Obtén el registro completo de un producto por ID: nombre, marca, ingredientes, alérgenos, Nutri-Score, grupo NOVA, imágenes y más.
Más información →Empieza a crear con la API de consulta masiva de alimentos
Gratis para empezar. Sin tarjeta de crédito.
Obtener clave API Ir al panel