Con una sola request obtenés todo el catálogo mayorista en tiempo real: precios en ARS y USDT, stock, tiempos de entrega, ofertas con su fecha de vencimiento y las portadas de cada juego.
public_catalog_v1 · REST · JSON → ¿Buscabas la API de pedidos (tu historial de compras)?| Dato | Valor |
|---|---|
| URL | https://mayoristas.epicodes.com.ar/api.php |
| Método | POST |
| Content-Type | application/json |
| API Key | Te la pasamos por privado. Va en el body de cada request. |
mayoristas.epicodes.com.ar. La vieja
epicodes.com.ar/mayoristas/... ya no funciona.
En el body (JSON) mandás solo dos campos:
action: siempre "public_catalog_v1"api_key: tu key{
"action": "public_catalog_v1",
"api_key": "TU_API_KEY"
}
Una request devuelve el catálogo entero (todas las combinaciones título × plataforma × licencia). No hace falta paginar ni hacer varias llamadas.
curl -X POST "https://mayoristas.epicodes.com.ar/api.php" \
-H "Content-Type: application/json" \
-d '{"action":"public_catalog_v1","api_key":"TU_API_KEY"}'
| Campo | Descripción |
|---|---|
ok | true si salió bien. |
catalog_version | Número de versión del catálogo (para caché, ver punto 6). |
catalog_version_iso | Fecha/hora de esa versión. |
ts | Timestamp de la respuesta. |
rate_limit | Tu estado de límites (ver punto 7). |
items | Array con todos los productos. |
| Campo | Tipo | Descripción |
|---|---|---|
provider_product_id | string | Identificador del juego. Ej: "ea-sports-fc-26". Las 4 variantes de un mismo juego comparten este valor. |
provider_variant_id | string | Identificador de la variante exacta (juego + plataforma + licencia). Ej: "ea-sports-fc-26-ps5-primario". Único en todo el catálogo: usá este como clave primaria de tu sync. |
sku | string | Mismo valor que provider_variant_id, por si tu sistema espera ese nombre. No es un campo distinto. |
title | string | Nombre del juego. |
platform | string | "PS4" o "PS5". |
license | string | "Primario" o "Secundario". |
title de tu
lado. Son inmunes a cambios cosméticos del nombre comercial: tildes, mayúsculas,
puntuación, apóstrofes o espacios de más no mueven el ID.
provider_product_id es además el mismo slug que ya ves dentro de
cover_url, así que podés cruzarlos.
| Campo | Tipo | Descripción |
|---|---|---|
price_ars | number · null | Precio en pesos, a valor mayorista. Si hay oferta, ya viene aplicada. |
price_usdt | number · null | Precio en USDT. |
Pueden venir null si esa variante no tiene precio cargado.
| Campo | Tipo | Descripción |
|---|---|---|
available_count | number | Unidades para entrega inmediata. |
stock_status | string | "instant" (hay stock: se entrega en el momento) o "up_to_6h" (sin stock: se puede pedir igual y queda pendiente hasta que conseguimos la cuenta). El valor conserva ese nombre por compatibilidad: el tiempo aproximado depende de la hora del pedido (ver los tiempos por franja). |
delivery_mode | string | Texto legible: "instantáneo" o "hasta 6h". El segundo también quedó fijo por compatibilidad: no lo muestres como plazo de entrega. |
delivery_eta (nuevo) | string | El tiempo para mostrar, el mismo que ves en el portal: "instantáneo" si hay stock y, si no, el aproximado para un pedido hecho ahora: "aprox 20m", "aprox 1h", "aprox 40m" o "desde las 12". Cambia con la hora del día, así que tomalo de la última respuesta. |
delivery_eta_minutes (nuevo) | number · null | Lo mismo en minutos: 0 si es instantáneo, 20, 60 o 40. Viene null de 22 a 12 hs ("desde las 12", hora argentina): se entrega cuando arranca el turno, no a un número de minutos de la compra. |
Si alguna vez no vienen delivery_eta / delivery_eta_minutes, usá stock_status. Cuando el tiempo cambia de franja también se mueve catalog_version.
| Campo | Tipo | Descripción |
|---|---|---|
on_sale | boolean | true si el producto está en oferta. |
offer_source | string · null | Origen de la oferta: "sony", "epicodes" o null. |
offer_ends_at | string ISO · null | Fecha y hora de fin de la oferta, en ISO 8601 con zona horaria. Ej: "2026-07-15T22:00:00-03:00". Es null cuando la oferta no tiene fecha de vencimiento. |
| Campo | Tipo | Descripción |
|---|---|---|
cover_url | string · null | Portada del juego (webp). |
cover_thumb_url | string · null | Miniatura (webp). |
on_sale: false → precio normal.on_sale: true + offer_source: "sony" + offer_ends_at con fecha → oferta de Sony que vence ese día/hora. Ideal para un contador o un “oferta hasta el 15/07”.on_sale: true + offer_source: "epicodes" + offer_ends_at: null → oferta nuestra, sin fecha de fin definida.price_ars / price_usdt
son el valor a precio mayorista (el que pagás vos). Si el producto está en oferta,
ya viene con la oferta aplicada — no tenés que calcular ningún porcentaje. El precio
de venta al público lo definís vos.
Cada respuesta trae catalog_version. Guardá el último
valor que procesaste: si en la próxima llamada viene el mismo número, el catálogo
no cambió y podés saltear el reprocesamiento. Cambia apenas se modifica un precio,
stock u oferta.
En cada respuesta, el objeto rate_limit te informa:
| Campo | Descripción |
|---|---|
limit_per_minute | Tu límite (60). |
user_remaining | Requests que te quedan este minuto. |
global_remaining | Cupo global compartido restante. |
reset_ms | Milisegundos hasta que se reinicia el contador. |
Si algo falla, ok viene en false
con un error y un code:
| Code | error | Significado |
|---|---|---|
| 401 | missing_api_key | No mandaste la key. |
| 403 | invalid_api_key | Key inválida o revocada. |
| 429 | rate_limited | Pasaste el límite. Incluye retry_after_ms con cuánto esperar. |
retry_after_ms
y reintentá. No reintentes en loop sin esperar.
stock_status / available_count.delivery_eta.on_sale + offer_ends_at.cover_url / cover_thumb_url.catalog_version.La estructura es la real; los precios y el stock del ejemplo son ilustrativos.
{
"ok": true,
"api": "public_catalog_v1",
"catalog_version": 1783382337162,
"catalog_version_iso": "2026-07-06T23:58:57.271Z",
"ts": "2026-07-07T00:03:29.365Z",
"rate_limit": {
"limit_per_minute": 60,
"user_remaining": 59,
"global_remaining": 299,
"reset_ms": 31349
},
"items": [
{
"provider_product_id": "ea-sports-fc-26",
"provider_variant_id": "ea-sports-fc-26-ps4-primario",
"sku": "ea-sports-fc-26-ps4-primario",
"title": "EA SPORTS FC 26",
"platform": "PS4",
"license": "Primario",
"price_ars": 10000,
"price_usdt": 7,
"delivery_mode": "instantáneo",
"stock_status": "instant",
"available_count": 12,
"on_sale": true,
"offer_source": "sony",
"offer_ends_at": "2026-07-15T22:00:00-03:00",
"cover_url": "https://epicodes.com.ar/cdn/covers/ea-sports-fc-26-ps4-md.webp",
"cover_thumb_url": "https://epicodes.com.ar/cdn/covers/ea-sports-fc-26-ps4-thumb.webp",
"delivery_eta": "instantáneo",
"delivery_eta_minutes": 0
},
{
"provider_product_id": "mortal-kombat-11",
"provider_variant_id": "mortal-kombat-11-ps4-primario",
"sku": "mortal-kombat-11-ps4-primario",
"title": "Mortal Kombat 11",
"platform": "PS4",
"license": "Primario",
"price_ars": 20000,
"price_usdt": 14,
"delivery_mode": "hasta 6h",
"stock_status": "up_to_6h",
"available_count": 0,
"on_sale": false,
"offer_source": null,
"offer_ends_at": null,
"cover_url": "https://epicodes.com.ar/cdn/covers/mortal-kombat-11-ps4-md.webp",
"cover_thumb_url": "https://epicodes.com.ar/cdn/covers/mortal-kombat-11-ps4-thumb.webp",
"delivery_eta": "aprox 20m",
"delivery_eta_minutes": 20
}
]
}