Ir al contenido principal

Documentación

API ez-catalog

Base de datos de productos argentinos. REST sobre HTTPS, JSON, una sola API key. Base URL: https://api.ez-catalog.huggian.com

Quick start

  1. Registrate en landing.huggian.com/catalog/signup — el plan Free no pide tarjeta.
  2. Guardá la API key que aparece en pantalla. No se muestra de nuevo. También llega por email.
  3. Mandala en el header X-Api-Key en cada request.
cURL
curl https://api.ez-catalog.huggian.com/v1/products/barcode/7790895000997 \
  -H "X-Api-Key: ezc_live_..."

Autenticación

Toda request a /v1/* requiere el header X-Api-Key. Las rutas /health e /i/{key} son públicas.

  • 401 Unauthorized — falta o key inválida.
  • 403 Forbidden — key válida pero sin permiso (tier muy bajo o sin write).
  • 429 Too Many Requests — rate limit excedido. Esperá el próximo minuto.

Planes y límites

Plan Rate limit Lectura Escritura Búsqueda full-text
Free 10 rpm Sí (barcode, brand, family, category) No No
Basic 60 rpm Sí (todo)
Pro 300 rpm Sí (todo)

Las reglas de rate limit son por API key, por minuto, ventana deslizante. Si se downgradea una key, el contador de uso mensual no se resetea — el reseteo es UTC 00:00 del día 1.

Errores

Todos los errores responden JSON con campo error. Algunos agregan message con contexto.

{
  "error": "full_text_search_not_available",
  "message": "Full-text search requires a Basic or Pro plan. Use /v1/products/barcode/:ean for barcode lookup."
}
  • 400 — body inválido.
  • 401 — auth faltante o inválida.
  • 403 — tier insuficiente.
  • 404 — recurso no existe.
  • 409 — conflicto (barcode/brand duplicado).
  • 429 — rate limit.
  • 500 — interno. Reintentar con backoff.

Productos

GET /v1/products/barcode/{ean} Free+

Buscar producto por código de barras EAN.

Response

{
  "id": "8a3e...",
  "barcode": "7790895000997",
  "barcode_type": "EAN13",
  "name": "Yerba Mate Taragüi 500g",
  "brand": "Taragüi",
  "brand_id": "f1c2...",
  "brand_logo_url": "https://api.ez-catalog.huggian.com/i/brands/taragui.png",
  "image_url": "https://api.ez-catalog.huggian.com/i/products/7790895000997.jpg",
  "category_id": "ab12...",
  "country_of_origin": "AR",
  "verified": true,
  "product_type": "good",
  "presentation": "500g",
  "unit_of_measure": "g",
  "ncm_code": "0903.00.10",
  "afip_category": null,
  "family_id": "c9d0...",
  "attributes": null,
  "source": "manual",
  "created_at": "2026-01-12T10:00:00Z",
  "updated_at": "2026-04-30T09:00:00Z"
}
  • Devuelve 404 si el EAN no está en el catálogo.
GET /v1/products/{id} Free+

Buscar producto por UUID.

Response

Mismo shape que /barcode/{ean}.
GET /v1/products/search?q=&brand=&family=&category=&page=&per_page= Basic+

Búsqueda full-text con filtros y paginación.

Response

{
  "items": [ /* Product[] */ ],
  "total": 142,
  "page": 1,
  "per_page": 20
}
  • page por defecto 1 (mín 1). per_page por defecto 20 (1..100).
  • brand, family, category aceptan UUID.
  • Free recibe 403 con error full_text_search_not_available.
POST /v1/products Basic+

Crear producto.

Request body

{
  "barcode": "7790895000997",
  "barcode_type": "EAN13",
  "name": "Yerba Mate Taragüi 500g",
  "description": null,
  "category_id": "ab12...",
  "brand": "Taragüi",
  "brand_id": null,
  "image_url": null,
  "ncm_code": "0903.00.10",
  "afip_category": null,
  "country_of_origin": "AR",
  "source": "manual",
  "presentation": "500g",
  "subcategory": null,
  "unit_of_measure": "g",
  "product_type": "good",
  "attributes": null
}

Response

201 Created → Product completo.
  • Solo `name` es obligatorio.
  • 409 barcode already exists si el EAN choca con otro producto.
  • 409 brand name already exists si la marca textual ya existe normalizada.
PUT /v1/products/{id} Basic+

Actualizar producto (todos los campos opcionales, parche).

Request body

{ "name": "Nuevo nombre", "verified": true }

Response

200 → Product actualizado.
DELETE /v1/products/{id} Basic+

Borrar producto.

Response

204 No Content.

Marcas

GET /v1/brands Free+

Listar marcas.

Response

[
  { "id": "f1c2...", "name": "Taragüi", "slug": "taragui", "logo_url": "...", "created_at": "..." }
]
GET /v1/brands/{id} Free+

Obtener marca por UUID.

Response

Brand.
POST /v1/brands Basic+

Crear marca (normaliza acentos, sufijos corporativos, mayúsculas).

Request body

{ "name": "La Serenísima", "logo_url": null }

Response

201 → Brand. 409 si la marca normalizada ya existe.

Familias

GET /v1/families Free+

Listar familias de producto (variantes de un mismo SKU base).

Response

ProductFamily[].
GET /v1/families/{id} Free+

Obtener familia por UUID.

Response

ProductFamily.

Categorías

GET /v1/categories Free+

Listar categorías (árbol jerárquico, `parent_id` apunta al padre o `null`).

Response

Category[].

Imágenes

GET /v1/products/{ean}/images Free+

Listar imágenes de un producto por EAN.

Response

{ "images": [ { "key": "products/7790.../v1.jpg", "url": "https://api.ez-catalog.huggian.com/i/products/..." } ] }
POST /v1/products/{ean}/images Basic+

Subir imagen (multipart/form-data con campo `file`).

Response

201 → { "key": "...", "url": "..." }.
  • Acepta JPEG, PNG, WebP. Tamaño máximo configurable por entorno.
GET /i/{key} Free+

Proxy de imagen pública. Sirve los bytes desde el bucket. Sin auth — la URL es la credencial.

Response

Image bytes (Content-Type real). Cacheable por CDN.
  • No requiere X-Api-Key.
  • El servicio rechaza keys que escapen del prefijo `products/`.

Health check

GET /health Free+

Healthcheck no autenticado.

Response

{ "ok": true }

Schemas

Product
{
  id: uuid,
  barcode: string | null,
  barcode_type: string,           // "EAN13" | "EAN8" | "UPC" | ...
  name: string,
  description: string | null,
  category_id: uuid | null,
  brand: string | null,
  brand_id: uuid | null,
  brand_logo_url: string | null,
  image_url: string | null,
  ncm_code: string | null,
  afip_category: string | null,
  country_of_origin: string,       // ISO 3166-1 alpha-2
  verified: boolean,
  source: string,                  // "manual" | "openff" | ...
  presentation: string | null,     // "500g", "1L", ...
  subcategory: string | null,
  unit_of_measure: string | null,  // "g" | "ml" | "u"
  family_id: uuid | null,
  product_type: string,            // "good" | "service"
  attributes: object | null,       // free-form JSON
  created_at: datetime,
  updated_at: datetime
}
Brand
{
  id: uuid,
  name: string,
  slug: string,                    // normalizado: sin acentos, lowercase
  logo_url: string | null,
  created_at: datetime
}
ProductFamily
{
  id: uuid,
  name: string,
  slug: string,
  brand_id: uuid | null,
  created_at: datetime
}
Category
{
  id: uuid,
  slug: string,
  name: string,
  parent_id: uuid | null,          // null para categorías raíz
  created_at: datetime
}