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.
curl -H "X-API-Key: food_•••" \
"https://foodbase.dev/v1/foods/search?q=banana&limit=20"
{
"data": [
{
"id": "034becf0-9aa7-c748-41aa-b71dd4df2bae",
"name_default": "Bananes",
"name_localized": "Bananas",
"brand": "Bio Village, Marque Repère",
"barcode": "3564707141710",
"category": "...,Fruits,Fruits tropicaux,Bananes",
"nutriscore": "a"
}
],
"total": 1000,
"limit": 20,
"offset": 0
} ¿Prefieres hacer clic? Prueba el explorador de productos o la zona de pruebas de fotos.
Qué obtienes
id
ID estable del alimento: úsalo con el resto de endpoints.
name_default
Nombre del producto en su idioma principal.
name_localized / category_localized
Traducciones en el lang solicitado (en, fr, es, de, it, bg) cuando existen; si no, el texto original.
brand
Marca o fabricante, si se conoce.
barcode
Código de barras EAN-13 / UPC, si está disponible.
category
Categoría de alimento de primer nivel.
total / limit / offset
Metadatos de paginación del conjunto completo de resultados.
Incluido en el plan gratuito: 100 solicitudes/día, sin tarjeta de crédito. 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/search?q=banana&limit=20" \
-H "X-API-Key: YOUR_API_KEY"
const res = await fetch("https://foodbase.dev/v1/foods/search?q=banana&limit=20", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
import requests
res = requests.get(
"https://foodbase.dev/v1/foods/search?q=banana&limit=20",
headers={"X-API-Key": "YOUR_API_KEY"},
timeout=30,
)
res.raise_for_status()
data = res.json()
Cómo funciona
Qué hace una petición de búsqueda
Una búsqueda es un único GET con el texto en q. Las palabras se comparan a la vez con el nombre del producto y con la marca, así que q=olympus milk encuentra productos cuyo nombre nunca dice Olympus, y la última palabra se trata como prefijo, de modo que milk olym ya coincide mientras el usuario todavía está escribiendo. Deben coincidir todas las palabras; no existe el OR. q debe tener entre 1 y 200 caracteres; una q ausente o vacía se rechaza antes de ejecutar ninguna búsqueda, así que protege tú el cuadro de búsqueda vacío.
Los productos que se llaman exactamente como lo que escribiste van primero, de modo que banana lista las bananas antes que los yogures de banana y greek yogurt lista los yogures griegos naturales antes que el resto. Después, los resultados se ordenan por relevancia y, cuando dos productos coinciden igual de bien, se lista primero el más popular. Una consulta de marca como pringles se salta el paso del nombre exacto y se queda en orden de popularidad; añadir una segunda palabra (una marca, un sabor) es la forma más rápida de subir un producto concreto.
Los nombres de categoría son un recurso de reserva, no parte de la coincidencia principal: solo cuando la búsqueda por nombre y marca no encuentra nada, la API busca productos cuya categoría contenga las palabras, en el idioma en que esté guardada la categoría. Por eso una consulta como Seeds & Related Snacks o Fruits tropicaux devuelve una lista, ordenada por popularidad, en lugar de un array vacío.
- El orden no es alfabético ni por id. No vuelvas a ordenar el array data en el cliente.
- Si la entrada es un GTIN de solo dígitos, envíala a la API de consulta de códigos de barras: resuelve las variantes con ceros a la izquierda (0000080042556 encuentra 80042556), cosa que una búsqueda de texto no hace.
Cómo leer una fila de resultado
Cada elemento de data es un FoodListItem, una forma más ligera que el registro completo: id, barcode, source, name_default, brand, category, image_url, nutriscore y nova_group, además de name_localized (siempre presente) y category_localized cuando se pasa un lang distinto del inglés. Aquí no hay nutrientes; la lista está pensada para pintar filas, no para hacer cálculos.
Varios campos admiten null, y la muestra lo refleja. El caramelo Roshen 'Banana land' (4f995c13-c0df-bfcc-b6ec-449d312def75) tiene image_url null y nova_group null; brand y barcode también pueden ser null. Los valores de nutriscore fuera de a–e, como la 'n' y la 'u' de la muestra, significan que no se calculó ninguna nota, así que muéstralos como «sin calificar» en lugar de la letra en bruto.
category es la ruta completa de categorías de Open Food Facts, separada por comas de lo general a lo específico ('...,Fruits tropicaux,Bananes' para las bananas de Bio Village). Muestra el último segmento como etiqueta, o el primero para una agrupación gruesa, y quita prefijos de idioma como pt: antes de mostrarlo. image_url es una foto de un colaborador en images.openfoodfacts.org, así que prepara un marcador de posición para el caso null.
Paginación, total e ids estables
limit va de 1 a 100 (por defecto 20) y offset de 0 a 10000. total es un recuento con tope: la API deja de contar en 1000 coincidencias, así que el total de 1000 de la muestra para q=banana con limit=3 significa «al menos 1000». Muéstralo como '1000+' y no calcules un número de última página a partir de él.
Pagina sumando limit a offset hasta que data devuelva menos de limit elementos. Como offset tiene un máximo de 10000, una consulta con más coincidencias necesita una q más acotada, no una página más profunda.
- Los ids son estables entre reimportaciones de FoodBase, así que guarda el id de una fila de búsqueda y resuélvelo más tarde con la API de base de datos de alimentos sin volver a buscar.
- El ranking se calcula en cada petición y la popularidad puede cambiar, por lo que un offset no es un cursor duradero. Guarda ids, no posiciones de página.
Buscar en otro idioma
lang vale en por defecto. Cada idioma busca en la entrada de ese idioma del array de nombres del producto cuando un voluntario o la traducción por IA de FoodBase ha aportado una, y recurre a name_default, el nombre tal como está impreso en la etiqueta, para los productos que no tienen esa entrada; la marca siempre forma parte de la coincidencia. Así, lang=bg llega a todos los productos, pero prefiere los nombres en búlgaro donde existen, y una etiqueta en cirílico se encuentra con cualquier lang.
El lang que pasas también da forma a la salida. name_localized contiene el nombre traducido: la muestra, solicitada con lang=en, convierte 'Puré de plátano y fresa' en 'Banana and Strawberry Puree' y recurre a name_default cuando no hay nada que traducir, como muestra 'Banana land'. Muestra name_localized cuando no sea null y name_default en caso contrario. category_localized no es un campo obligatorio y no aparece en la muestra, así que léelo a la defensiva.
Autocompletado y el traspaso a registros completos
La búsqueda es el endpoint adecuado para un cuadro de escritura predictiva. Aplica debounce a las pulsaciones, pide limit=8 o 10 y cancela la petición en curso cuando el usuario vuelva a escribir, para que una respuesta lenta anterior no pise a una más reciente. Cada pulsación que llega a la API es una petición contra tu cuota diaria, así que el debounce es tanto un control de costes como un detalle de UX.
Cuando el usuario elige una fila, llama a la API de base de datos de alimentos con el id para obtener el registro completo, o a la API de nutrición si solo necesitas la tabla de nutrientes por 100 g. Una búsqueda más una petición de detalle sale más barato que traer registros completos para cada sugerencia.
La respuesta de búsqueda capturada no lleva cabecera Cache-Control, a diferencia de los registros de alimentos, que se pueden cachear durante 300 segundos. Cachea tú los resultados de búsqueda, con clave en la combinación exacta de q, lang, limit y offset; los mismos prefijos cortos se repiten constantemente en un cuadro de escritura predictiva. X-RateLimit-Second-Remaining indica cuánta ráfaga queda y, ante un 429, la cabecera Retry-After indica cuántos segundos esperar.
Qué puedes crear
Selectores de alimentos y autocompletado
Da vida al cuadro de búsqueda de un contador de calorías o una app de recetas: resultados mientras se escribe sobre 6,1 M de productos.
Enriquecimiento de catálogos
Asocia tus propios SKU con registros de alimentos canónicos por nombre o marca.
Feeds de descubrimiento
Explora por categoría o palabra clave para crear listas de alimentos seleccionadas.
Límites por plan
| Free | 100 solicitudes/día compartidas entre las claves de la cuenta; 1 solicitud/s por clave, ráfaga de 5. El tope de 25/día del desglose de nutrientes no se aplica a la búsqueda. |
|---|---|
| Starter | 5000 solicitudes/día; 5 solicitudes/s por clave, ráfaga de 25. |
| Pro | 25.000 solicitudes/día; 20 solicitudes/s por clave, ráfaga de 100. |
| Enterprise | 100.000 solicitudes/día; 50 solicitudes/s por clave, ráfaga de 250. |
Cada respuesta incluye cabeceras X-RateLimit-* con el presupuesto restante; cada 429 incluye Retry-After . Comparar planes →
Errores y cómo gestionarlos
401 — Falta la cabecera X-API-Key, tiene un formato incorrecto o pertenece a una clave revocada.
Revisa el nombre de la cabecera y la clave en tu panel. No reintentes hasta corregirlo.
422 — q falta o está vacía, tiene más de 200 caracteres, limit está fuera de 1 a 100, offset está fuera de 0 a 10000, o lang no es uno de en, fr, es, de, it, bg.
Lee el array details del cuerpo Validation Error para ver qué parámetro falló y corrige la petición en el cliente. Nunca reintentes un 422 sin cambios.
429 — Se agotó la ráfaga por segundo de la clave o se consumió la cuota diaria de la cuenta.
Espera el número de segundos de Retry-After y reintenta una vez. En un flujo de autocompletado, descarta la pulsación obsoleta en lugar de ponerla en cola.
Comparada con otras API
Un buscador de propósito general o un catálogo de e-commerce indexa texto; la búsqueda de FoodBase indexa productos alimentarios, así que cada coincidencia llega con el código de barras, la ruta de categorías, el Nutri-Score y el grupo NOVA adjuntos, y con un id que se resuelve en una tabla de nutrientes con una petición más. La contrapartida es el alcance: busca en nombres de producto y marcas, con los nombres de categoría solo como recurso de reserva, y no en todos los campos del registro, y un lang distinto del inglés busca en los nombres localizados donde existen y en los nombres de la etiqueta en el resto de casos, sin traducir tu consulta. No hay biblioteca cliente que instalar ni índice que construir de tu lado: un GET y un objeto JSON. Si estás comparando proveedores de datos alimentarios, /compare/food-database-apis muestra qué cubre cada uno.
Preguntas frecuentes
¿Por qué total devuelve exactamente 1000? +
El recuento tiene tope. Se deja de contar en 1000 coincidencias, así que 1000 significa «al menos tantas». Muéstralo como '1000+' y pagina por offset hasta que data devuelva menos de limit filas, en lugar de dividir total entre limit.
¿Qué devuelve la API cuando nada coincide? +
Un 200 con un array data vacío, no un 404. Trata el array vacío como tu estado de «sin resultados». Como todas las palabras de q deben coincidir, lo habitual para recuperarse es quitar la palabra menos específica o la más propensa a erratas antes de mostrarlo.
¿Puedo buscar solo por marca o por categoría? +
Por marca, sí: la marca forma parte de aquello con lo que se compara cada palabra de q, así que q=Hacendado lista los productos de esa marca y añadir una palabra de producto acota la lista. Los nombres de categoría solo se prueban cuando la búsqueda por nombre y marca no devuelve nada, así que q=Fruits tropicaux devuelve los productos archivados en esa categoría, ordenados por popularidad y no por relevancia.
¿Un resultado de búsqueda incluye nutrientes? +
No. Las filas de búsqueda tienen la forma más ligera FoodListItem, sin campos de nutrientes. Pasa el id de la fila a la API de nutrición al seleccionarla, y no para cada sugerencia, porque el plan gratuito permite 25 desgloses de nutrientes al día.
¿Qué lang debería pasar una app búlgara? +
lang=bg busca en los nombres localizados en búlgaro y devuelve name_localized en búlgaro cuando existe traducción. Las etiquetas introducidas en cirílico como nombre por defecto se encuentran con lang=en, porque en busca en el nombre por defecto sea cual sea la escritura. Consulta primero bg y recurre a en si el resultado está vacío.
¿El ranking es el mismo que en el sitio web de FoodBase? +
Sí. El explorador de productos en /products y las páginas de categorías en /categories/ funcionan sobre el mismo endpoint, así que puedes probar una consulta ahí y esperar el mismo orden en tu app para la misma q y lang. La búsqueda no usa créditos de IA; cada llamada es una petición contra la cuota diaria.
Descubre más endpoints
API de consulta de códigos de barras
Escanea o escribe un código de barras y obtén el producto. Resuelve cualquier código EAN-13 o UPC a un registro de alimento (marca, categoría, Nutri-Score y resumen nutricional) en una sola petición. Pensada para apps con escáner.
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 →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 →Empieza a crear con la API de búsqueda de alimentos
Gratis para empezar. Sin tarjeta de crédito.
Obtener clave API Ir al panel