Empezar en un minuto
Todas las direcciones cuelgan de https://elegate.joinet.com, siempre por HTTPS y siempre con el método GET. Pega esto en una terminal cambiando TU_LLAVE y ya tienes datos:
curl -H "X-API-Key: TU_LLAVE" \
"https://elegate.joinet.com/api/v1/productos?por_pagina=3"Si la llave es correcta, la respuesta empieza con "ok": true. Si algo falla, empieza con "ok": false y trae el motivo.
Autenticación
Cada petición necesita una llave. Se manda en una cabecera, de cualquiera de estas dos formas, la que le acomode a tu programa:
X-API-Key: jelg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Authorization: Bearer jelg_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxLa llave se muestra una sola vez. En el servidor solo se guarda su huella criptográfica, así que no se puede recuperar después: si se pierde, se revoca y se genera otra.
Trátala como contraseña. No la pongas en código que corra en un navegador ni la subas a un repositorio: quien la tenga puede leer todo el catálogo.
Cada llave lleva su propio registro de uso —cuántas peticiones, cuándo fue la última y desde qué dirección— y puede revocarse o ponerse con fecha de vencimiento en cualquier momento.
Forma de las respuestas
Siempre es JSON con Content-Type: application/json. Las respuestas correctas traen "ok": true; los errores traen "ok": false, una clave error estable para programar contra ella, y un mensaje en español para leerlo.
Dos detalles que conviene saber al procesar el JSON:
- Los campos con decimales (
precio,cantidad_caja,precio_regular_oferta) llegan entre comillas, como"168.0000". Es para no perder precisión; conviértelos conparseFloato equivalente. - Un campo que no tiene dato llega como
null, nunca se omite. Así siempre puedes contar con que la llave existe.
Límites y frescura del dato
Cada llave tiene un tope de peticiones por hora. Toda respuesta trae estas cabeceras:
| Cabecera | Qué dice |
|---|---|
X-RateLimit-Limit | Tu tope por hora. |
X-RateLimit-Remaining | Cuántas peticiones te quedan en esta hora. |
X-RateLimit-Reset | Segundos que faltan para que el contador vuelva a cero. |
X-Request-Id | Identificador de esta llamada. Si reportas un problema, mándalo: con él se encuentra la petición exacta en la bitácora. |
Si se agota, la respuesta es 429 e incluye Retry-After con los segundos exactos que hay que esperar: no hace falta adivinar.
El sincronizador revisa el punto de venta cada minuto y hace una pasada completa a las 3 de la mañana. Ojo con una diferencia que confunde: solo escribe los artículos que cambiaron. Entonces:
ultima_revision_pos(en/estatus) — cuándo se revisó el POS por última vez. Se mueve cada minuto, haya cambios o no. Es lo que dice si el servicio está vivo.synced_at(en cada producto) — cuándo se escribió ese artículo. Si un producto no ha cambiado en dos días, susynced_ates de hace dos días, y está bien: significa que su dato sigue siendo el mismo que tiene el POS.modificado_pos(en cada producto) — cuándo cambió el artículo dentro del POS.
Dicho de otro modo: un synced_at viejo no quiere decir dato viejo, quiere decir dato que no ha cambiado. Para saber si la cadena está corriendo, mira ultima_revision_pos.
Todas las fechas se entregan en formato ISO 8601 con zona horaria (2026-08-01T14:37:37.000Z, es decir UTC). El portal las muestra convertidas a horario de Guadalajara.
El máximo por página es 200. Para recorrer todo el catálogo, pagina con pagina; para mantenerte al día sin bajar todo, filtra con desde.
Endpoints
Son cuatro. Cualquier otro método que no sea GET responde 405.
/api/v1/estatusSalud del servicio, datos de tu llave y conteos del catálogo. Es la forma más rápida de comprobar que una llave sirve y qué tan fresco está el dato.
curl -H "X-API-Key: TU_LLAVE" "https://elegate.joinet.com/api/v1/estatus"{
"ok": true,
"servicio": "joinet-elegate-api",
"version": "1.0.0",
"llave": {
"nombre": "Joinet interno",
"prefijo": "jelg_c7e1b2c3",
"incluye_costos": true,
"limite_hora": 2000
},
"catalogo": {
"total": 3699, "publicados": 2710, "privados": 989,
"con_stock": 1975, "descontinuados": 405,
"ultima_revision_pos": "2026-08-02T05:12:41.224Z",
"ultimo_dato_nuevo": "2026-08-01T20:37:40.872Z",
"ultimo_cambio_pos": "2026-08-01T14:37:37.000Z"
},
"hora_servidor": "2026-08-02T04:39:20.011Z"
}/api/v1/categoriasLas 81 categorías presentes en el catálogo ELE GATE, ordenadas por número de artículos. El codigo que devuelve se usa tal cual en el filtro categoria del listado.
{
"ok": true,
"total": 81,
"categorias": [
{ "codigo": "JUGUE", "nombre": "Juguetes", "productos": 277, "con_stock": 149 },
{ "codigo": "AHOGA", "nombre": "Artículos para el hogar", "productos": 264, "con_stock": 141 },
{ "codigo": "CABLE", "nombre": "Cables y conectores", "productos": 194, "con_stock": 122 }
]
}/api/v1/productosEl listado. Todos los parámetros son opcionales y se combinan entre sí.
| Parámetro | Tipo | Por omisión | Qué hace |
|---|---|---|---|
q | texto | — | Busca el texto en descripción, SKU y claves alternas. |
categoria | texto | — | Código o nombre de categoría. Los códigos salen de /categorias. |
status | texto | todos | publish, private o draft. |
skus | texto | — | Hasta 200 SKU separados por coma. Trae solo esos, en una sola llamada. |
campos | texto | todos | Qué campos devolver, separados por coma. Aligera muchísimo la respuesta. |
sucursal | texto | — | Una, varias con coma, o un grupo: todas · web · tiendas · cedis. Agrega stock_sucursal a cada producto. |
con_stock | 1 / 0 | 0 | Con 1 devuelve solo lo que tiene existencia. Si mandas sucursal, mide la existencia de esa sucursal. |
con_imagenes | 1 / 0 | 0 | Con 1 agrega el arreglo imagenes a cada producto del listado. |
descontinuado | 1 / 0 | ambos | Con 0 excluye los descontinuados; con 1 trae solo esos. |
desde | fecha ISO | — | Solo lo modificado o sincronizado a partir de esa fecha. |
orden | texto | sku | sku · descripcion · precio · precio_desc · stock · stock_desc · venta_desc · modificado_desc · alta_desc |
pagina | entero | 1 | Número de página. Empieza en 1. |
por_pagina | entero | 50 | Resultados por página, de 1 a 200. |
curl -H "X-API-Key: TU_LLAVE" \
"https://elegate.joinet.com/api/v1/productos?q=cable&con_stock=1&orden=venta_desc&por_pagina=1"{
"ok": true,
"paginacion": { "total": 170, "pagina": 1, "por_pagina": 1, "paginas": 170 },
"productos": [
{
"sku": "ADP.HDTV.RJ45",
"descripcion": "Extensor repetidor hdmi de 60 metros a cable ethernet rj45…",
"ficha_tecnica": "Es un dispositivo que permite transmitir señales…",
"status": "publish",
"precio": "168.0000",
"precios_lista": { "1": 168, "2": 165, "3": 162, "4": 153, "5": 153, "6": 133 },
"en_oferta": false,
"stock_total": 114,
"stock_web": 217,
"stock_sucursales": { "independencia": 103, "septiembre": 84, "cotilla": 30, "corona": 0 },
"marca": "ELE-GATE",
"categoria": "Redes",
"categoria_codigo": "REDES",
"codigos_barras": ["0.64688"],
"cantidad_caja": "100.0000",
"unidad": "PZA",
"clave_sat": "43222608",
"garantia": true,
"venta_total": 579,
"costos": { "c1": 14, "c2": 0, "c3": 0, "c4": 0, "ultimo": 14 },
"proveedores": { "1": "ELE GATE", "2": null, "3": null },
"modificado_pos": "2026-08-01T14:37:37.000Z",
"synced_at": "2026-08-01T20:37:40.872Z"
}
]
}El bloque paginacion te dice cuántos resultados hay en total y cuántas páginas son con el tamaño que pediste. Cuando pagina se pasa del final, productos llega vacío: ese es el punto para dejar de pedir.
Pedir solo los campos que ocupas
Una página completa de 200 productos pesa unos 464 KB, y buena parte son fichas técnicas. Si solo necesitas precio y existencia, con campos la misma página baja a 26 KB: 95% menos.
https://elegate.joinet.com/api/v1/productos?campos=sku,precio,stock_total,stock_sucursales&por_pagina=200El sku siempre se incluye aunque no lo pidas, porque sin él la respuesta no identifica nada. Un campo que no exista devuelve 400 con la lista completa de los válidos.
Varios SKU en una sola llamada
En lugar de pedir producto por producto, mándalos juntos. Máximo 200 por petición, y no distingue mayúsculas:
https://elegate.joinet.com/api/v1/productos?skus=WI.62,CH.60,PT.10Fotos en el listado
Con con_imagenes=1 cada producto trae su arreglo imagenes, igual que en el detalle. Es la forma de armar un catálogo con fotos sin hacer una llamada por artículo:
https://elegate.joinet.com/api/v1/productos?con_imagenes=1&con_stock=1&por_pagina=100Buscar por código de barras
q busca al mismo tiempo en la descripción, el SKU, las claves alternas y los códigos de barras. Un código escaneado se pega tal cual:
https://elegate.joinet.com/api/v1/productos?q=0.64688/api/v1/productos/{sku}Un artículo con todo lo disponible: los mismos campos del listado, más todas sus fotos y la descripción larga que se muestra en la tienda. El SKU no distingue mayúsculas de minúsculas.
curl -H "X-API-Key: TU_LLAVE" "https://elegate.joinet.com/api/v1/productos/FES.26.8"{
"ok": true,
"producto": { "sku": "FES.26.8", "descripcion": "Listón decorativo…", "…": "los 41 campos" },
"imagenes": [
{ "url": "https://cdn.joinet.com/…/FES.26.8.webp", "posicion": 0, "es_principal": true },
{ "url": "https://cdn.joinet.com/…/FES.0.webp", "posicion": 1, "es_principal": false }
],
"contenido_web": {
"descripcion_web": "<h1>Listón decorativo brilloso, 2.7 metros…</h1>",
"videos": [],
"updated_at": "2026-08-01T08:30:15.999Z"
}
}En imagenes, la que trae es_principal: true es la portada; las demás vienen en el orden en que se muestran. contenido_web llega como null si ese artículo no tiene descripción propia en la tienda.
/api/v1/skusLa lista completa de SKU vigentes, sin paginar y sin más datos. Pesa poco y sirve para una cosa que ningún otro endpoint resuelve: enterarte de las bajas.
Por qué existe. El filtro desde te trae altas y cambios, pero si un artículo se borra o deja de ser ELE GATE, simplemente deja de aparecer — nadie te avisa. Si mantienes una copia, se te queda con productos fantasma para siempre.
La solución: pide esta lista, compárala contra tus SKU y lo que no venga aquí, dalo de baja.
{
"ok": true,
"total": 3699,
"generado": "2026-08-02T05:31:12.004Z",
"skus": ["0269", "0515", "0649", "1717", "ADP.HDTV.RJ45", "..."]
}# Qué SKU tengo yo que ya no existan del otro lado
curl -s -H "X-API-Key: TU_LLAVE" "https://elegate.joinet.com/api/v1/skus" \
| jq -r '.skus[]' | sort > vigentes.txt
comm -23 mis-skus.txt vigentes.txt # esto es lo que hay que dar de bajaListas de precio
Cada producto trae hasta diez listas en precios_lista. No son categorías de cliente: son cortes por cantidad. Mientras más piezas lleva el cliente, menor es el precio por pieza.
| Lista | Aplica a partir de | Qué es |
|---|---|---|
1 | 1 pieza | Precio público de menudeo. Es el mismo valor del campo precio. |
2 | 2 piezas | Primer escalón por volumen. |
3 | 3 a 5 piezas | Segundo escalón. |
4 | 6 a 8 piezas | Tercer escalón. |
5 | 9 a 11 piezas | Cuarto escalón. |
6 | 12 piezas o más | Precio de mayoreo. Es el más bajo de la escalera. |
7 a 10 | — | Existen en el punto de venta pero no se usan en la web. Casi siempre llegan en 0. |
Un 0 no es gratis: significa que esa lista no tiene precio capturado. Al calcular, ignora las listas en 0 y usa la anterior que sí tenga valor.
"precios_lista": { "1": 168, "2": 165, "3": 162, "4": 153, "5": 153, "6": 133, "7": 0, "8": 0, "9": 0, "10": 0 }
// Un cliente que lleva 7 piezas paga la lista 4: $153 cada una.
// Uno que lleva 20 paga la lista 6: $133 cada una.Existencias por sucursal
Las existencias se pueden ver de dos maneras, y las dos están disponibles al mismo tiempo.
Todas juntas: no hay que pedir nada
Cada producto ya trae el desglose completo, siempre. stock_sucursales son las cuatro tiendas y stock_cedis los cuatro centros de distribución:
"stock_total": 7081,
"stock_web": 8732,
"stock_sucursales": { "independencia": 1651, "septiembre": 4808, "cotilla": 2273, "corona": 0 },
"stock_cedis": { "alcalde": 0, "madero": 0, "prisciliano": 0, "cedis4": 0 }Una sucursal en particular
Con el parámetro sucursal. Cada producto agrega el campo stock_sucursal con la existencia de lo que pediste, y si combinas con con_stock=1 el filtro se aplica a esa sucursal, no al total:
curl -H "X-API-Key: TU_LLAVE" \
"https://elegate.joinet.com/api/v1/productos?sucursal=cotilla&con_stock=1&orden=stock_desc&por_pagina=3"{
"ok": true,
"paginacion": { "total": 1399, "pagina": 1, "por_pagina": 3, "paginas": 466 },
"sucursales": ["cotilla"],
"productos": [
{
"sku": "JUG.G888",
"stock_sucursal": 8495,
"stock_total": 9717,
"stock_sucursales": { "independencia": 131, "septiembre": 1091, "cotilla": 8495, "corona": 0 }
},
{
"sku": "CH.60",
"stock_sucursal": 2273,
"stock_total": 8732,
"stock_sucursales": { "independencia": 1651, "septiembre": 4808, "cotilla": 2273, "corona": 0 }
}
]
}Con orden=stock_desc, el orden también respeta la sucursal pedida: arriba queda lo que más hay en Cotilla, no lo que más hay en total.
Varias sucursales sumadas
Separadas por coma. stock_sucursal devuelve la suma de esas ubicaciones:
https://elegate.joinet.com/api/v1/productos?sucursal=cotilla,septiembre&con_stock=1&orden=stock_desc"sucursales": ["cotilla", "septiembre"],
"productos": [
{ "sku": "JUG.G888", "stock_sucursal": 9586 }, // 8495 en Cotilla + 1091 en Septiembre
{ "sku": "CH.60", "stock_sucursal": 7081 } // 2273 + 4808
]Grupos con nombre
Tres atajos para no escribir la lista completa:
| Grupo | Incluye | Para qué sirve |
|---|---|---|
todas | las 4 tiendas + los 4 CEDIS | Todo junto, en un solo número. Es la existencia real en toda la empresa. |
web | septiembre · cotilla · independencia | Las tres tiendas que sí surten pedidos en línea. Es el mismo criterio del campo stock_web. |
tiendas | independencia · septiembre · cotilla · corona | Las cuatro tiendas, sin contar centros de distribución. |
cedis | alcalde · madero · prisciliano · cedis4 | Solo los centros de distribución. |
https://elegate.joinet.com/api/v1/productos?sucursal=todas&con_stock=1&orden=stock_desc
https://elegate.joinet.com/api/v1/productos?sucursal=web&con_stock=1
https://elegate.joinet.com/api/v1/productos?sucursal=cedis&con_stock=1Los grupos también se combinan con sucursales sueltas: sucursal=cedis,cotilla suma los cuatro centros de distribución más la tienda de Cotilla.
Nombres válidos
Tiendas: independencia · septiembre · cotilla · corona
Centros de distribución: alcalde · madero · prisciliano · cedis4
Grupos: todas · web · tiendas · cedis
Si mandas un nombre que no existe, la respuesta es 400 con la lista completa de opciones en el mensaje, así que no hace falta adivinar.
Ejemplo completo: inventario de una tienda
Todo lo que hay hoy en Independencia, ordenado de mayor a menor existencia, listo para pegar en una hoja de cálculo:
curl -s -H "X-API-Key: TU_LLAVE" \
"https://elegate.joinet.com/api/v1/productos?sucursal=independencia&con_stock=1&orden=stock_desc&por_pagina=200" \
| jq -r '.productos[] | [.sku, .descripcion, .stock_sucursal, .precio] | @csv' \
> inventario-independencia.csvDiccionario de campos
Estos son los campos que trae cada producto, tanto en el listado como en el detalle.
| Campo | Tipo | Significado | Ejemplo |
|---|---|---|---|
sku | texto | Clave del artículo en el POS. Es el identificador único y el que se usa en /productos/{sku}. | "WI.62" |
descripcion | texto | Descripción principal, tal como está capturada en el POS. | "1pza Cable trifásico…" |
descripcion_alterna | texto | Segunda descripción. Suele venir vacía. | null |
ficha_tecnica | texto | Ficha larga: características, especificaciones, modo de uso y cuidados. Texto plano con saltos de línea. | "Cable trifásico de…" |
status | texto | Estado en la tienda: publish (visible), private (oculto) o draft. | "publish" |
block_reason | texto | Motivo por el que el artículo está bloqueado, cuando aplica. | null |
precio | número | Precio público vigente. Es el mismo que precios_lista["1"]. | "168.0000" |
precios_lista | objeto | Precios de las listas 1 a 10, con el número de lista como llave. Un 0 significa que esa lista no tiene precio. | {"1":168,"6":133} |
tipo_precio | texto | Letra de la política de precio del POS (A, B, C…). null = precio manual. | null |
precio_valido | sí/no | false cuando el POS no tiene un precio utilizable para el artículo. | true |
en_oferta | sí/no | El artículo está marcado en oferta. | false |
en_remate | sí/no | El artículo está marcado en remate. | false |
precio_regular_oferta | número | Precio tachado que acompaña a la oferta. | "168.0000" |
leyenda_oferta | texto | Texto de la promoción capturado en el POS. | "Precio Normal" |
stock_total | entero | Existencia total según el campo del POS, incluye ubicaciones no vendibles. | 114 |
stock_web | entero | Existencia vendible en línea: suma de Septiembre, Cotilla e Independencia. Puede no coincidir con stock_total porque son dos cálculos distintos del POS. | 217 |
stock_sucursales | objeto | Existencia por tienda: independencia, septiembre, cotilla y corona. | {"cotilla":30,…} |
stock_cedis | objeto | Existencia en centros de distribución: alcalde, madero, prisciliano y cedis4. | {"alcalde":0,…} |
marca | texto | Nombre de la marca. | "ELE-GATE" |
marca_codigo | texto | Código de la marca en el POS. | "ELEGA" |
marca_imagen | texto | URL del logotipo de la marca. | "https://cdn.joinet…" |
categoria | texto | Nombre de la línea o categoría. | "Redes" |
categoria_codigo | texto | Código de la categoría. Es el que se manda en el filtro categoria. | "REDES" |
codigos_barras | lista | Hasta cuatro códigos de barras del artículo. | ["0.64688"] |
claves_alternas | texto | Claves alternas separadas por coma: claves del fabricante y equivalencias. La búsqueda q también busca aquí. | null |
cantidad_caja | número | Piezas por caja o empaque. | "100.0000" |
unidad | texto | Unidad de medida. | "PZA" |
clave_sat | texto | Clave del producto en el catálogo del SAT, para facturación. | "43222608" |
garantia | sí/no | El artículo maneja garantía. | true |
descontinuado | sí/no | Está marcado como descontinuado en el POS. | false |
resurtir | sí/no | Está marcado para resurtido. | true |
bloqueado_busqueda | sí/no | Excluido del buscador de la tienda. | false |
imagen_pos | texto | URL de la imagen principal registrada en el POS. Para todas las fotos, usa el detalle. | "https://cdn.joinet…" |
fecha_creacion_pos | fecha | Fecha de alta del artículo en el POS. | "2025-08-14T00:00:00Z" |
modificado_pos | fecha | Última modificación registrada en el POS. Es el campo natural para detectar cambios. | "2026-08-01T14:37:37Z" |
synced_at | fecha | Momento exacto en que este servidor tomó el registro del POS. | "2026-08-01T20:37:40Z" |
Campos internos
Estos solo se entregan si la llave tiene habilitado el acceso interno. Con una llave sin ese permiso simplemente no aparecen en la respuesta —no llegan vacíos, no llegan en cero: no llegan.
| Campo | Tipo | Significado | Ejemplo |
|---|---|---|---|
costos | objeto | Costos c1 a c4 y costo último. Un 0 significa que ese costo no está capturado. | {"c1":14,"ultimo":14} |
venta_total | entero | Piezas vendidas acumuladas históricas. | 579 |
venta_sucursales | objeto | Piezas vendidas acumuladas por tienda. | {"cotilla":70,…} |
proveedores | objeto | Proveedor 1, 2 y 3. El POS admite hasta tres por artículo. | {"1":"ELE GATE","2":null} |
ultima_compra_pos | fecha | Fecha de la última compra registrada en el POS. | "2024-08-09T00:00:00Z" |
Errores
| HTTP | error | Qué pasó |
|---|---|---|
400 | orden_invalido · fecha_invalida | Un parámetro no tiene el formato esperado. El mensaje dice cuál y qué se acepta. |
401 | sin_llave · llave_invalida | No mandaste llave, o no corresponde a ninguna registrada. |
403 | llave_revocada · llave_vencida | La llave existe pero ya no sirve. |
404 | no_encontrado | El SKU no existe dentro del catálogo ELE GATE. |
405 | metodo_no_permitido | Se usó POST, PUT, PATCH o DELETE. Esta API es de solo lectura. |
429 | limite_excedido | Se superó el límite de peticiones por hora de la llave. |
{
"ok": false,
"error": "limite_excedido",
"mensaje": "Superaste 2000 peticiones en esta hora. Espera al siguiente bloque horario."
}Casos de uso
Qué hay disponible para vender hoy
https://elegate.joinet.com/api/v1/productos?con_stock=1&status=publish&orden=venta_descBuscar por texto, código de barras o clave del fabricante
El parámetro q busca al mismo tiempo en la descripción, en el SKU y en las claves alternas.
https://elegate.joinet.com/api/v1/productos?q=hdmi
https://elegate.joinet.com/api/v1/productos?q=WI.62Todo lo de una categoría
https://elegate.joinet.com/api/v1/categorias
https://elegate.joinet.com/api/v1/productos?categoria=REDES&por_pagina=200Solo lo que cambió desde ayer
Es la forma correcta de mantener una copia al día sin volver a bajar los 3,699 artículos. Guarda la fecha de tu última corrida y mándala en desde.
https://elegate.joinet.com/api/v1/productos?desde=2026-08-01T00:00:00Z&por_pagina=200Lo más vendido que ya se está acabando
https://elegate.joinet.com/api/v1/productos?con_stock=1&orden=venta_desc&por_pagina=50Ejemplos por lenguaje
Terminal (curl)
curl -s -H "X-API-Key: TU_LLAVE" \
"https://elegate.joinet.com/api/v1/productos?con_stock=1&por_pagina=5" | jq '.productos[] | {sku, precio, stock_total}'PHP
<?php
function elegate($ruta) {
$ch = curl_init("https://elegate.joinet.com/api/v1" . $ruta);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["X-API-Key: TU_LLAVE"],
CURLOPT_TIMEOUT => 30,
]);
$r = json_decode(curl_exec($ch), true);
curl_close($ch);
if (empty($r["ok"])) throw new Exception($r["mensaje"] ?? "Error de la API");
return $r;
}
$r = elegate("/productos?con_stock=1&por_pagina=20");
echo "Hay {$r['paginacion']['total']} artículos con existencia\n";
foreach ($r["productos"] as $p) {
printf("%-16s %-50s $%8.2f stock %d\n",
$p["sku"], mb_substr($p["descripcion"], 0, 50),
(float) $p["precio"], $p["stock_total"]);
}JavaScript (Node, no navegador)
Esta API no se llama desde el navegador, a propósito. No manda cabeceras CORS, así que un fetch desde una página web falla — y está bien que falle: para que funcionara, la llave tendría que ir escrita en el código de la página, donde cualquiera la lee abriendo el inspector. Llámala siempre desde tu servidor y que él le pase los datos a la página.
const API = "https://elegate.joinet.com/api/v1";
const LLAVE = "TU_LLAVE";
async function elegate(ruta) {
const r = await fetch(API + ruta, { headers: { "X-API-Key": LLAVE } });
const d = await r.json();
if (!d.ok) throw new Error(d.mensaje);
return d;
}
const { productos, paginacion } = await elegate("/productos?q=cable&con_stock=1");
console.log(`${paginacion.total} resultados`);
productos.forEach(p =>
console.log(p.sku, parseFloat(p.precio).toFixed(2), p.stock_total));Python
import requests
API = "https://elegate.joinet.com/api/v1"
CAB = {"X-API-Key": "TU_LLAVE"}
r = requests.get(f"{API}/productos", headers=CAB,
params={"con_stock": 1, "orden": "venta_desc", "por_pagina": 20},
timeout=30)
d = r.json()
if not d["ok"]:
raise SystemExit(d["mensaje"])
for p in d["productos"]:
print(f'{p["sku"]:<16} {p["descripcion"][:50]:<52} '
f'${float(p["precio"]):>8.2f} stock {p["stock_total"]}')Bajar todo el catálogo
Se pagina de 200 en 200 hasta que una página llegue vacía. Son 19 peticiones para los 3,699 artículos, muy por debajo del límite por hora.
pagina=1
: > catalogo.jsonl
while :; do
r=$(curl -s -H "X-API-Key: TU_LLAVE" \
"https://elegate.joinet.com/api/v1/productos?por_pagina=200&pagina=$pagina")
n=$(echo "$r" | jq '.productos | length')
[ "$n" -eq 0 ] && break
echo "$r" | jq -c '.productos[]' >> catalogo.jsonl
echo "página $pagina · $n artículos"
pagina=$((pagina + 1))
done
echo "Total: $(wc -l < catalogo.jsonl) artículos"Lo mismo en Python, con reintentos
import requests, json, time
API, CAB = "https://elegate.joinet.com/api/v1", {"X-API-Key": "TU_LLAVE"}
pagina, todos = 1, []
while True:
for intento in range(3):
try:
d = requests.get(f"{API}/productos", headers=CAB,
params={"por_pagina": 200, "pagina": pagina},
timeout=60).json()
break
except requests.RequestException:
time.sleep(2 ** intento)
else:
raise SystemExit(f"Falló la página {pagina}")
if not d["productos"]:
break
todos += d["productos"]
print(f'página {pagina} · {len(todos)}/{d["paginacion"]["total"]}')
pagina += 1
with open("catalogo.json", "w", encoding="utf-8") as f:
json.dump(todos, f, ensure_ascii=False, indent=2)Probar sin escribir código
Dos archivos listos para descargar, con las 14 peticiones de ejemplo ya armadas. Solo pones tu llave en la variable y le das enviar.
| Archivo | Para qué | Cómo se usa |
|---|---|---|
| elegate.postman.json | Postman | Postman → Import → arrastra el archivo. Luego edita la variable llave de la colección. |
| elegate.http | VS Code · JetBrains | Ábrelo con la extensión REST Client, cambia @llave y pulsa "Send request" arriba de cada petición. |
Cómo conseguir una llave
Las llaves las emite el equipo de sistemas de Joinet. Al pedirla, di para qué es y desde dónde se va a usar, para asignarle el límite adecuado y decidir si lleva acceso a datos internos.
Al crearla se define: nombre (para identificarla después), si incluye datos internos (costos, ventas y proveedores), el límite por hora y si vence en cierta fecha. Todo eso se puede consultar en cualquier momento en /api/v1/estatus, y una llave se puede revocar al instante si se filtra.
Lo que esta API no hace
Para que nadie pierda tiempo buscándolo:
- No escribe nada. No crea pedidos, no aparta mercancía, no descuenta existencias ni modifica precios. Cualquier método que no sea
GETresponde 405. - No es una API de pedidos. Ver que hay 40 piezas no las reserva; entre tu consulta y tu venta alguien más pudo haberlas comprado en mostrador.
- Solo cubre ELE GATE. Un SKU de otra marca responde 404 aunque exista en la tienda.
- No manda avisos. No hay webhooks ni notificaciones: tú consultas cuando lo necesitas, con
desdepara lo que cambió y/skuspara las bajas. - No se llama desde el navegador. Sin CORS, y por seguridad de la llave (ver el ejemplo de JavaScript).
- No tiene precios especiales por cliente. Las listas son por cantidad, iguales para todos.
Versiones y cambios
La versión va en la dirección: /api/v1/. El compromiso es simple:
- Dentro de v1 nunca se quita ni se renombra un campo, ni cambia el significado de uno existente. Tu integración no se va a romper sola.
- Sí se pueden agregar campos y parámetros nuevos. Escribe tu código para ignorar lo que no conozca.
- Si algún día hiciera falta un cambio incompatible, saldría como
/api/v2/yv1seguiría funcionando mientras haya quien la use.
| Versión | Fecha | Cambios |
|---|---|---|
1.0.0 | 1 ago 2026 | Primera versión: estatus, categorías, listado, detalle y SKU vigentes; filtros por sucursal, campos, SKU múltiples, imágenes y descontinuados. |