mercado_publico_cl

Cliente Ruby para la API pública de Mercado Público (Chile).

Gem Version Build Coverage Ruby

Gema Ruby para consumir la API de compras públicas del Estado de Chile (Mercado Público): licitaciones, órdenes de compra, proveedores y organismos públicos. El código y la API pública de la librería están en inglés; la documentación está en español.

Tabla de contenidos

Instalación

Agrega la gema a tu Gemfile:

gem "mercado_publico_cl"

O instálala manualmente:

gem install mercado_publico_cl

Requisitos: Ruby >= 3.2 (probado en 3.2, 3.3, 3.4 y 4.0).

¿Qué es Mercado Público?

Mercado Público es la plataforma electrónica de compras públicas del Estado de Chile, operada por la Dirección ChileCompra (https://www.mercadopublico.cl). A través de su API pública es posible consultar licitaciones, órdenes de compra, proveedores y organismos compradores.

Obtener tu ticket

Cada usuario o aplicación necesita un ticket (token de acceso) para consumir la API. Para obtenerlo:

  1. Ingresa a https://www.mercadopublico.cl con tu Clave Única o credenciales de proveedor/organismo.
  2. Ingresa a tu Escritorio y selecciona Servicios Web.
  3. Genera un ticket nuevo. Guardalo de forma segura (no lo subas a tu repo).

El ticket es personal. No lo compartas en commits ni en logs.

Configuración

En Rails (con el generator)

rails g mercado_publico_cl:install

Esto crea config/initializers/mercado_publico_cl.rb con todas las opciones documentadas. Editalo y exporta tu ticket vía ENV.

En Rails (manual)

config/initializers/mercado_publico_cl.rb:

MercadoPublicoCl.configure do |c|
  c.ticket   = ENV.fetch("MERCADO_PUBLICO_TICKET")
  c.timeout  = 10
  c.logger   = Rails.logger
  c.base_url = "https://api.mercadopublico.cl"
end

Fuera de Rails

require "mercado_publico_cl"

MercadoPublicoCl.configure do |c|
  c.ticket = ENV.fetch("MERCADO_PUBLICO_TICKET")
end

Variables disponibles

Variable Tipo Default Descripción
ticket String ENV["MERCADO_PUBLICO_TICKET"] Token de acceso
timeout Integer 10 Timeout HTTP en segundos
logger Logger / nil nil Si se setea, registra cada request (sin el ticket)
base_url String https://api.mercadopublico.cl Solo https (se permite http únicamente hacia localhost, para mocks)
time_zone String "America/Santiago" Zona horaria para la fecha default de las consultas
max_retries Integer 0 Reintentos automáticos en errores transitorios
retry_base Float 0.5 Segundos de la primera espera (se duplica con backoff)
retry_max_wait Integer 30 Cap del backoff
retry_on Array [:rate_limit, :server_error, :timeout] Categorías reintentables (acepta strings, se normalizan)

MercadoPublicoCl.configuration devuelve la instancia actual. MercadoPublicoCl.reset! la reinicia (útil en specs).

Fechas en hora de Chile

La API indexa por fecha chilena. Cuando una consulta necesita fecha y no le pasas una, la gema usa MercadoPublicoCl.today: el "hoy" calculado en America/Santiago (vía TZInfo si está disponible — Rails lo trae — o con offset fijo -04:00 como fallback). Esto evita que un worker corriendo en UTC consulte, durante la noche chilena, un día que aún no tiene datos.

MercadoPublicoCl.today   # => Date del día actual en Chile
MercadoPublicoCl::Tender.where(status: :active)  # usa esa fecha por defecto

Reintentos automáticos

Por defecto los reintentos están desactivados (max_retries = 0). Para producción se recomienda activarlos:

MercadoPublicoCl.configure do |c|
  c.max_retries    = 3
  c.retry_base     = 0.5
  c.retry_max_wait = 30
  c.retry_on       = %i[rate_limit server_error timeout]
end
  • Backoff exponencial con jitter: primer reintento ~retry_bases, segundo ~retry_base * 2s, tercero ~retry_base * 4s, todo capeado en retry_max_wait.
  • Retry-After: si la API devuelve un 429 con header Retry-After: N, se respeta ese valor (capeado en retry_max_wait; también disponible en RateLimitError#retry_after).
  • Errores de red incluidos: conexiones rechazadas o reseteadas (ECONNREFUSED, ECONNRESET, SSL, EOF...) se traducen a ApiError y se reintentan bajo la categoría :server_error.
  • Lo que NO se reintenta: InvalidTicketError (401/403), NotFoundError (404), InvalidQueryError, ApiError 4xx y redirecciones 3xx. Reintentar errores deterministas solo retrasa el feedback.
  • Logging: cada reintento se loguea como WARN si hay logger configurado.

Uso rápido

tender = MercadoPublicoCl::Tender.find("1057-25-LE25")
tender.status                 # => :published
tender.estimated_amount       # => 1500000.0

orders = MercadoPublicoCl::PurchaseOrder.where(status: :accepted) # hoy (hora chilena)
orders.each { |o| puts o.code }

supplier = MercadoPublicoCl::Supplier.find_by_rut("70.017.820-k")
agencies = MercadoPublicoCl::PublicAgency.all

Listados vs detalle: summary?

La API de Mercado Público devuelve dos shapes distintos según cómo consultes:

Método Qué devuelve Campos por item
Tender.all / .active / .where(...) Listado liviano 3-4 (code, name, status, closing_date)
Tender.find(code) Detalle completo 60+ (items, fechas, comprador, etc.)
PurchaseOrder.all / .where(...) Listado liviano 3 (code, name, status)
PurchaseOrder.find(code) Detalle completo 60+

Esto no es una decisión de la gema — es así como responde el server. Probablemente por performance: un día puede tener 4.500 licitaciones activas o 18.000 OCs. El detalle completo de todas sería de varios MB.

¿Por qué importa?

Si llamas .active y lees un campo que no viene en el resumen, te va a devolver nil — pero no significa que el dato no exista, solo que no lo pediste:

t = MercadoPublicoCl::Tender.active.first
t.code                # => "1000-8-LE26"  ✓ vino en el resumen
t.complaints_count    # => nil  ✗ no vino — pero la licitación SÍ tiene reclamos
t.items               # => []   ✗ no vino — pero la licitación SÍ tiene items

¿Cómo distingo uno del otro?

Con summary?:

t = MercadoPublicoCl::Tender.active.first
t.summary?            # => true → solo tienes el resumen

t = MercadoPublicoCl::Tender.find("1000-8-LE26")
t.summary?            # => false → tienes el detalle completo

Patrón: hidratar listado con detalle

Si quieres trabajar con el detalle completo de cada item, hay que pedirlo:

codes = MercadoPublicoCl::Tender.active.map(&:code)            # 1 request
details = codes.map { |c| MercadoPublicoCl::Tender.find(c) }    # 1 request por cada

⚠️ Cuidado con el límite de 10.000 requests/día por ticket. Hidratar los items de un listado se llama "1+N requests": una por el listado más una por cada detalle. Para 4.500 licitaciones activas son 4.501 requests — casi medio cupo diario gastado en una operación. Filtra lo más posible (agency_code, supplier_code, status) antes de hidratar.

Licitaciones (Tenders)

Consultas disponibles

Endpoint de la API real Método de la gema
GET /servicios/v1/publico/licitaciones.json?codigo=CODE Tender.find(code)
GET /servicios/v1/publico/licitaciones.json?estado=todos Tender.all
GET /servicios/v1/publico/licitaciones.json?estado=activas Tender.active
GET ...?fecha=ddmmYYYY Tender.where(date:)
GET ...?estado=adjudicada Tender.where(status: :awarded)
GET ...?fecha=ddmmYYYY&estado=adjudicada Tender.where(date:, status: :awarded)
GET ...?fecha=ddmmYYYY&proveedor=17793 Tender.where(date:, supplier_code: 17793)
GET ...?fecha=ddmmYYYY&CodigoOrganismo=6945 Tender.where(date:, agency_code: 6945)

Estados

Código Símbolo Descripción
5 :published Publicada
6 :closed Cerrada
7 :deserted Desierta
8 :awarded Adjudicada
18 :revoked Revocada
19 :suspended Suspendida
:active Atajo estado=activas
:all Atajo estado=todos

Atributos del objeto Tender

Nota: Tender.all / .active / .where devuelven resúmenes (code, name, status, closing_date). El detalle completo solo aparece llamando a Tender.find(code). Usa tender.summary? para detectar uno u otro.

Top-level

Atributo Tipo Descripción
code String CodigoExterno
name String Nombre
description String Descripcion
status / status_code Symbol / Int Estado
tender_type / tender_type_code Symbol / String Tipo (L1, LE, LP, ...)
currency / currency_code Symbol / String Moneda
estimated_amount Float MontoEstimado
estimated_amount_type / _code Symbol / Int Presupuesto o referencia
tender_payment_method / _code Symbol / Int Modalidad de pago
evaluation_time_unit / _code Symbol / Int Unidad de evaluación
contract_duration_time_unit / _code Symbol / Int Unidad duración contrato
administrative_act / _code Symbol / Int Tipo de acto
supplier_code Integer CodigoProveedor
agency_code Integer CodigoOrganismo
creation_date Date FechaCreacion
closing_date Date FechaCierre
publication_date Date FechaPublicacion
award_date Date FechaAdjudicacion
informed? Boolean Informada (1=Sí, 0=No)
public? / private? Boolean CodigoTipo
requires_contraloria? Boolean TomaRazon
public_technical_offers? Boolean EstadoPublicidadOfertas
has_contract? Boolean Contrato (doc oficial: 1=Sí, 0=No; solo 1 es true)
public_works? Boolean Obras (¡1=No, 2=Sí!)
visible_amount? Boolean VisibilidadMonto
allows_subcontracting? Boolean SubContratacion
extends_deadline? Boolean ExtensionPlazo
template_based? Boolean EsBaseTipo
renewable? Boolean EsRenovable
complaints_count Integer CantidadReclamos
days_to_close Integer DiasCierreLicitacion
funding_source String FuenteFinanciamiento
contracting_restriction String ProhibicionContratacion
bip_code String / nil CodigoBIP
raw Hash JSON original sin tocar

Objetos anidados

Atributo Clase Acceso a campos
buyer TenderBuyer buyer.code, buyer.name, buyer.unit_*, buyer.user_name, buyer.user_role, ...
items Array<TenderItem> items.first.product_name, .quantity, .unit_of_measure, .award
dates TenderDates dates.creation, .closing, .publication, .award, .technical_opening, .economic_opening, .estimated_award, .site_visit, .documents_delivery, ... (16 fechas)
award TenderAward / nil award.resolution_number, .resolution_date, .award_type_code, .bidders_count, .act_url (URL del acta; solo cuando está adjudicada)
payment_contact TenderContact / nil payment_contact.name, .email
contract_contact TenderContact / nil contract_contact.name, .email, .phone

Shortcuts (back-compat con la API anterior)

tender.agency_code y tender.agency_name redirigen a tender.buyer. tender.creation_date, closing_date, publication_date, award_date redirigen a tender.dates.

Proveedor adjudicado

El proveedor ganador viene en la Adjudicacion de cada ítem, no en la de nivel licitación. La gema lo expone directo:

t = MercadoPublicoCl::Tender.find("1219241-3-LR26")
t.awarded?               # => true
t.awarded_supplier_rut   # => "97.006.000-6"
t.awarded_supplier_name  # => "BANCO DE CREDITO E INVERSIONES"
t.award.act_url          # => URL del acta de adjudicación

Ejemplos detallados

# Buscar por código
tender = MercadoPublicoCl::Tender.find("1057-25-LE25")
tender.public_works?    # => true
tender.raw["Items"]     # acceso al payload original

# Todas las licitaciones publicadas hoy (hora chilena, fecha implícita)
MercadoPublicoCl::Tender.where(status: :published)

# Filtrar por organismo + día
MercadoPublicoCl::Tender.where(date: MercadoPublicoCl.today, agency_code: 6945)

Órdenes de compra (Purchase Orders)

Consultas disponibles

Endpoint API real Método de la gema
GET /servicios/v1/publico/ordenesdecompra.json?codigo=CODE PurchaseOrder.find(code)
GET ...?estado=todos PurchaseOrder.all
GET ...?fecha=ddmmYYYY[&estado=...&proveedor=...&CodigoOrganismo=...] PurchaseOrder.where(...)

Estados

Código Símbolo Descripción
4 :sent_to_supplier Enviada a proveedor
5 :in_process En proceso (solo lectura — la API no lo acepta como filtro)
6 :accepted Aceptada
9 :cancelled Cancelada
12 :received Recepción conforme
13 :pending_receipt Pendiente recepción
14 :partially_received Recepción parcial
15 :incomplete_receipt Recepción conforme incompleta
:all Atajo estado=todos

supplier_status (estado del lado del proveedor) usa una tabla de códigos distinta — ver Estado de OC según proveedor en los anexos.

Atributos del objeto PurchaseOrder

Nota: PurchaseOrder.all / .where devuelven resúmenes con solo code, name, status. El detalle completo aparece con PurchaseOrder.find(code). Usa oc.summary? para distinguirlos.

Top-level

Atributo Tipo Descripción
code String Codigo
name, description String
status / status_code / status_label Symbol / Int / String Estado de la OC
supplier_status / _code / _label Symbol / Int / String Estado según proveedor
tender_code String / nil CodigoLicitacion — licitación de origen
purchase_order_type / _code / _label Symbol / Int / String Tipo
currency / currency_code Symbol / String
total_amount Float Total (con IVA)
net_amount Float TotalNeto
tax_amount Float Impuestos
iva_percentage Float PorcentajeIva
discount_amount, charge_amount Float Descuentos, Cargos
dispatch_type / _code Symbol / Int
payment_type / _code Symbol / Int
funding_source String Financiamiento
country String Pais
has_items? Boolean TieneItems
rating Float PromedioCalificacion
rating_count Integer CantidadEvaluacion
raw Hash JSON original

Objetos anidados

Atributo Clase Campos clave
buyer POBuyer code, name, unit_rut, unit_name, unit_comuna, activity, contact
vendor POVendor code, name, branch_rut, branch_name, address, comuna, region, contact
items Array<POItem> product_name, quantity, unit_price, total, currency
dates PODates creation, sent, acceptance, cancellation, last_modified

Shortcuts

supplier_code, supplier_name, agency_code, agency_name, creation_date, sent_date, acceptance_date, cancellation_date, last_modified_at redirigen a los objetos anidados correspondientes.

Ejemplos

order = MercadoPublicoCl::PurchaseOrder.find("1057-25-SE25")
order.status        # => :accepted
order.total_amount  # => 250000.0

MercadoPublicoCl::PurchaseOrder.where(date: MercadoPublicoCl.today, supplier_code: 17_793)

Proveedores (Suppliers)

Buscar por RUT

supplier = MercadoPublicoCl::Supplier.find_by_rut("70.017.820-k")
supplier.name   # => "Proveedor Demo SpA"
supplier.code   # => 12345

Atributos: code, name, rut, raw.

El RUT se acepta con o sin puntos (siempre con guión y dígito verificador): "70.017.820-k" y "70017820-k" son equivalentes — la gema lo canonicaliza con puntos antes de consultar. Formatos no reconocibles levantan InvalidQueryError.

Organismos públicos (Public Agencies)

MercadoPublicoCl::PublicAgency.all          # listado completo (899 organismos)
MercadoPublicoCl::PublicAgency.find(6945)   # filtra por código (ver advertencia abajo)
MercadoPublicoCl::PublicAgency.directory    # Hash code→agency, ideal para múltiples lookups

Atributos: code, name, raw.

⚠️ Advertencia de performance en PublicAgency.find

La API no expone un endpoint "buscar organismo por código". Por eso find internamente hace un .all (request HTTP con los 899 organismos) y filtra en memoria — una request por cada llamada a find.

Si necesitas resolver varios códigos, usa .directory que devuelve un Hash y hace una sola request:

# ❌ Mal: 10 lookups = 10 requests con 899 items cada uno
codes = [7086, 1224636, 7193, 7212, 1824441, 1806837, 6959, 7265, 7038, 7313]
agencies = codes.map { |c| MercadoPublicoCl::PublicAgency.find(c) }

# ✓ Bien: 1 request, lookups O(1)
directory = MercadoPublicoCl::PublicAgency.directory
agencies = codes.map { |c| directory[c] }

Si ya tienes la lista en memoria, puedes saltarte la request:

list = MercadoPublicoCl::PublicAgency.all
directory = MercadoPublicoCl::PublicAgency.directory(list: list)

Esto importa especialmente con el límite de 10.000 requests/día.

Anexos: tablas de códigos

Tipos de licitación

Código Símbolo
L1 :public_tender_under_100_utm
LE :public_tender_100_to_1000_utm
LP :public_tender_1000_to_2000_utm
LQ :public_tender_2000_to_5000_utm
LR :public_tender_over_5000_utm
LS :personal_services_tender
A1/B1/J1/F1/E1 variantes (ver Enums::TenderType)
CO/B2/A2/E2/H2/I2 privadas
D1/C1/C2/F2/F3 obras públicas
G1/G2 concesión
R1 otra
CA compra coordinada
SE trato directo / selección

Tipos de orden de compra

Código Símbolo Etiqueta API
1 :automatic OC
2 :d1 D1
3 :c1 C1
4 :f3 F3
5 :g1 G1
6 :r1 R1
7 :ca CA
8 :se SE
9 :framework_agreement CM
10 :fg FG
11 :tl TL
12 :microcompra MC
13 :compra_agil AG
14 :coordinated_purchase CC

Monedas

Código Símbolo
CLP :clp
CLF :clf
USD :usd
UTM :utm
EUR :eur

Modalidad de pago (licitación)

Código Símbolo
1 :net_30
2 :net_30_60_90
3 :same_day
4 :annual
5 :net_60
6 :monthly
7 :on_delivery
8 :bimonthly
9 :milestone_based
10 :quarterly

Modalidad de pago (orden de compra)

Código Símbolo
1 :net_15_on_invoice
2 :net_30_on_invoice
39 :other
46 :net_50_on_invoice
47 :net_60_on_invoice
48 :net_45
49 :over_30_days

Tipo de despacho

Código Símbolo
7 :to_address
9 :per_program
12 :other
14 :pickup_from_warehouse
20 :air_courier
21 :ground_courier
22 :to_be_agreed

Unidad de tiempo

Código Símbolo
1 :hours
2 :days
3 :weeks
4 :months
5 :years

Acto administrativo

Código Símbolo
1 :authorization
2 :resolution
3 :other
4 :decree
5 :agreement

Estado de OC según proveedor

CodigoEstadoProveedor / EstadoProveedor usa su propia tabla, distinta a la del estado de la orden ("Recepción Conforme" es 12 para la OC pero 7 para el proveedor). La tabla no está documentada oficialmente; estos pares se confirmaron contra la API real (jun-2026). Códigos no confirmados devuelven nil en supplier_status — el código y el label crudos siempre están en supplier_status_code / supplier_status_label.

Código Símbolo Label de la API
1 :new_order Nueva orden de compra
4 :accepted Aceptada
5 :cancelled Cancelada
7 :received Recepción Conforme

Tipo de monto estimado

Código Símbolo
1 :available_budget
2 :reference_price
3 :not_estimable

Campos binarios

La API retorna 0/1 para la mayoría de los flags. Una excepción importante es Obras donde 1=No y 2=Sí — la gema invierte esto en public_works?.

Manejo de errores

Toda la jerarquía cuelga de MercadoPublicoCl::Error.

Error Cuándo
ConfigurationError Problema de configuración
MissingTicketError Se llama a un recurso sin ticket configurado
InvalidTicketError HTTP 401/403 o Codigo 401/403
NotFoundError HTTP 404 o Codigo 404
InvalidQueryError Combinación de parámetros inválida
RateLimitError HTTP 429 o Codigo 429
TimeoutError Timeout de socket o HTTP 408
ApiError Cualquier 5xx o Codigo no manejado. Trae status_code y response_body

Códigos con formato inválido lanzan ApiError (HTTP 500)

La API de Mercado Público responde con HTTP 500 cuando recibe un codigo con formato inválido (caracteres no esperados, shape distinto al documentado, etc.), en vez de devolver 400/404. Esto se traduce a ApiError en la gema.

Cuando el código tiene el formato correcto pero no existe, la API devuelve un listado vacío y find retorna nil. Resumen:

Caso Comportamiento de find
Código bien formado y existe Devuelve el objeto
Código bien formado y no existe Devuelve nil
Código con formato inválido Lanza ApiError(status_code: 500)

La gema no pre-valida el formato del código intencionalmente, porque el shape exacto varía entre tipos de recurso y podría cambiar. La responsabilidad de validar el input recae en el consumidor:

begin
  tender = MercadoPublicoCl::Tender.find(user_input)
  render_tender(tender) if tender
rescue MercadoPublicoCl::ApiError => e
  if e.status_code == 500
    render_error("Código con formato inválido")
  else
    raise
  end
end

O directamente validando antes de llamar:

unless user_input.match?(/\A\d+-\d+-[A-Z]+\d+\z/i)
  return render_error("Formato esperado: organismo-número-tipoAÑO")
end
MercadoPublicoCl::Tender.find(user_input)

Sincronización con tu base de datos

Si tu app corre ActiveJob, puedes subclasear los jobs incluidos:

class TenderSyncJob < MercadoPublicoCl::Jobs::SyncTenders
  queue_as :default

  def process(tender)
    Tender.upsert!(
      code: tender.code,
      status: tender.status,
      payload: tender.raw
    )
  end
end

# Programar un sync diario — sin `date:` usa el "hoy" chileno,
# evaluado al EJECUTAR el job (no al encolarlo)
TenderSyncJob.perform_later(status: :published)

Evita perform_later(date: Date.today): esa fecha se evalúa al encolar y con la TZ del server. Si el job corre después de medianoche (o el server está en UTC), sincronizarías el día equivocado.

La gema no toca tu modelo de DB — solo orquesta. Lo mismo aplica para MercadoPublicoCl::Jobs::SyncPurchaseOrders.

Acceso al payload original

Cada recurso expone .raw con el Hash JSON sin transformar, por si necesitas campos que la gema no mapea:

tender = MercadoPublicoCl::Tender.find("1057-25-LE25")
tender.raw["Items"]
tender.raw["Unidades"]

Testing en tu app

La gema usa HTTParty internamente. Para stubear las requests en tus specs puedes usar WebMock:

stub_request(:get, %r{api\.mercadopublico\.cl/.+/licitaciones\.json})
  .to_return(status: 200, body: { "Listado" => [] }.to_json,
             headers: { "Content-Type" => "application/json" })

Seguridad

El ticket es una credencial — tratala como tal

  • No lo subas al repositorio — usa ENV o tu password manager. La generación del initializer ya lo asume.
  • No lo registres en logs. La gema redacta el ticket de inspect/to_s de Configuration y del response_body de cualquier ApiError. Aun así, no lo incluyas en cadenas que registres tú mismo.
  • No lo compartas entre ambientes. Mercado Público te permite generar varios tickets — uno por ambiente (dev/staging/prod).

Redirecciones HTTP deshabilitadas

La gema configura HTTParty con no_follow: true. Si el servidor responde con un 3xx, la gema no sigue el redirect automáticamente — lanza ApiError para que investigues. Esto evita un ataque MITM donde un upstream comprometido podría redirigir el request a otro host.

Mercado Público no usa redirects en producción, así que esto no afecta el funcionamiento normal.

TLS / certificados

La gema mantiene la verificación SSL por default (verify: true). No expone una opción para desactivarla. Si necesitas un sandbox local con certificados autofirmados, lo más limpio es configurar tu sistema (CA trust store) en lugar de tocar la gem.

PII en logs

Configuration.logger, si lo configuras, registra los parámetros de cada request (sin el ticket). Esos parámetros pueden contener RUTs de proveedores, códigos de organismo y similar — información sujeta a la Ley 19.628 chilena de protección de datos personales.

Recomendación: en producción usa nivel INFO o más alto y rota logs. Si tu pipeline manda logs a un servicio externo (Datadog, Loggly, etc.), configura redacción de PII ahí.

base_url y path son confianza interna

  • configuration.base_url lo configuras tú en el initializer. Si lo apuntás a un host no oficial, ahí van tus tickets. Como el ticket viaja en la query string, la gema rechaza http:// con ConfigurationError (solo se permite hacia localhost/127.0.0.1, para mocks y tests) — en texto plano el ticket quedaría expuesto a cualquier intermediario.
  • El parámetro path de Client#get no debe venir de input de usuario. Solo los Resources::* lo usan internamente con paths hardcoded. Hacer MercadoPublicoCl.client.get(user_input, ...) desde un controller es un SSRF — no lo hagas.

Tamaño de response sin límite

PurchaseOrder.all puede traer ~19.000 items en un día. No hay límite configurable de tamaño de response. Si necesitas hard-cap para evitar OOM, ponlo en tu capa de aplicación (timeouts, memory limit del worker).

Limitaciones y notas

  • Límite diario por ticket: 10.000 solicitudes. Cada ticket de acceso cuenta con un límite diario de 10.000 solicitudes. Este límite no es modificable y tiene como finalidad resguardar la estabilidad del servicio. Las personas usuarias se comprometen a no exceder ni eludir estas limitaciones. El uso excesivo o abusivo de la API podrá derivar en la suspensión temporal o el bloqueo permanente del acceso.
  • Sin caching interno. Cada llamada va a la red; cachear es responsabilidad del consumidor.
  • El ticket es personal. No lo compartas en commits ni en logs.
  • Los listados se acotan al día consultado. La API solo devuelve resultados del día indicado en fecha.
  • Solo JSON. No se implementa XML ni JSONP.

Contribuciones

Los pull requests son bienvenidos — revisa CONTRIBUTING.md para el setup del entorno, el flujo de trabajo (test primero) y las guías del proyecto.

Licencia

MIT — ver LICENSE.txt.


Fuente de la información sobre límites y condiciones de uso de la API: https://www.chilecompra.cl/api/